[gradle] 01/08: Imported Upstream version 1.8

Eugene Zhukov eugene-guest at moszumanska.debian.org
Fri May 16 07:04:41 UTC 2014


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

eugene-guest pushed a commit to branch 1.8
in repository gradle.

commit 510b0c4550a413d4495b7fc0d80239fe131aa6de
Author: Eugene Zhukov <jevgeni.zh at gmail.com>
Date:   Wed Apr 23 08:40:22 2014 +0000

    Imported Upstream version 1.8
---
 build.gradle                                       |  21 +-
 buildSrc/build.gradle                              |   2 +-
 .../main/groovy/org/gradle/build/BuildTypes.groovy |   2 +-
 .../main/groovy/org/gradle/build/JarJarJar.groovy  |   6 +-
 .../build/docs/SampleElementValidator.groovy       |   2 -
 .../build/docs/UserGuideTransformTask.groovy       |   5 +-
 .../build/docs/dsl/docbook/JavadocConverter.java   |  17 +
 .../docs/dsl/source/ExtractDslMetaDataTask.groovy  |   4 +-
 .../dsl/source/GenerateDefaultImportsTask.java     | 148 ++++
 .../docs/model/SimpleClassMetaDataRepository.java  |  10 +-
 .../docs/dsl/docbook/JavadocConverterTest.groovy   |  10 +
 .../model/SimpleClassMetaDataRepositoryTest.groovy |  26 +-
 config/checkstyle/checkstyle.xml                   |   3 +
 gradle/buildReceipt.gradle                         |   1 +
 gradle/dependencies.gradle                         |   6 +-
 gradle/groovyProject.gradle                        |  20 +-
 gradle/integTest.gradle                            |   8 +-
 gradle/testGroupings.gradle                        |  13 +
 gradle/versioning.gradle                           |   4 +
 gradle/wrapper.gradle                              |  28 +-
 gradle/wrapper/gradle-wrapper.properties           |   4 +-
 settings.gradle                                    |   5 +
 subprojects/announce/announce.gradle               |   3 +-
 .../api/plugins/announce/AnnouncePlugin.groovy     |   5 +-
 .../announce/AnnouncePluginExtension.groovy        |  36 +-
 .../announce/internal/AnnouncerFactory.groovy      |   3 -
 .../internal/DefaultAnnouncerFactory.groovy        |   3 -
 .../plugins/announce/internal/NotifySend.groovy    |   3 -
 .../api/plugins/announce/internal/Twitter.groovy   |   8 +-
 .../announce/AnnouncePluginExtensionTest.groovy    |   8 +-
 .../api/plugins/announce/AnnouncePluginTest.groovy |   9 +-
 .../announce/BuildAnnouncementsPluginTest.groovy   |   4 +-
 .../internal/DefaultAnnouncerFactoryTest.groovy    |   7 +-
 subprojects/antlr/antlr.gradle                     |   2 +-
 .../org/gradle/api/plugins/antlr/AntlrPlugin.java  |   6 +-
 .../plugins/antlr/AntlrSourceVirtualDirectory.java |   2 -
 .../org/gradle/api/plugins/antlr/AntlrTask.java    |  14 +-
 .../internal/AntlrSourceVirtualDirectoryImpl.java  |   2 -
 .../api/plugins/antlr/internal/GenerationPlan.java |   2 -
 .../antlr/internal/GenerationPlanBuilder.java      |  12 +-
 .../plugins/antlr/internal/GrammarDelegate.java    |  10 +-
 .../antlr/internal/GrammarFileMetadata.java        |   6 +-
 .../plugins/antlr/internal/GrammarMetadata.java    |   8 +-
 .../plugins/antlr/internal/MetadataExtracter.java  |  10 +-
 .../gradle/api/plugins/antlr/internal/XRef.java    |   8 +-
 .../api/plugins/antlr/AntlrPluginTest.groovy       |   8 +-
 .../base-services-groovy.gradle                    |   2 +-
 .../main/groovy/org/gradle/api/specs/AndSpec.java  |   1 -
 .../groovy/org/gradle/api/specs/AndSpecTest.java   |   4 +-
 subprojects/base-services/base-services.gradle     |   4 +-
 .../main/java/org/gradle/api/GradleException.java  |   2 -
 .../src/main/java/org/gradle/api/JavaVersion.java  |   4 +
 .../main/java/org/gradle/api/internal/Factory.java |   4 +-
 .../java/org/gradle/api/internal/Transformers.java |  48 ++
 .../api/internal/project/ServiceRegistry.java      |   4 +-
 .../java/org/gradle/api/specs/CompositeSpec.java   |   1 -
 .../main/java/org/gradle/api/specs/NotSpec.java    |   1 -
 .../src/main/java/org/gradle/api/specs/OrSpec.java |   1 -
 .../src/main/java/org/gradle/api/specs/Spec.java   |   1 -
 .../java/org/gradle/internal/LazyIterable.java     |  33 -
 .../internal/classloader/CachingClassLoader.java   |  66 ++
 .../internal/classloader/ClassLoaderFactory.java   |  47 ++
 .../internal/classloader/ClassLoaderHierarchy.java |  21 +
 .../internal/classloader/ClassLoaderSpec.java      |  45 ++
 .../internal/classloader/ClassLoaderVisitor.java   |  52 ++
 .../gradle/internal/classloader/ClasspathUtil.java | 101 +++
 .../classloader/DefaultClassLoaderFactory.java     | 111 +++
 .../internal/classloader/FilteringClassLoader.java | 275 ++++++++
 .../classloader/MultiParentClassLoader.java        | 126 ++++
 .../classloader/MutableURLClassLoader.java         |  89 +++
 .../classloader/TransformingClassLoader.java       |  63 ++
 .../internal/concurrent/ServiceLifecycle.java      | 118 ++++
 .../org/gradle/internal/jvm/JavaHomeException.java |   3 -
 .../java/org/gradle/internal/jvm/JavaInfo.java     |   3 -
 .../src/main/java/org/gradle/internal/jvm/Jre.java |  26 +
 .../src/main/java/org/gradle/internal/jvm/Jvm.java |  80 ++-
 .../org/gradle/internal/os/OperatingSystem.java    |  51 +-
 .../internal/reflect/DirectInstantiator.java       |   2 +-
 .../org/gradle/internal/reflect/Instantiator.java  |   2 +-
 .../internal/reflect/JavaReflectionUtil.java       | 346 ++++++++-
 .../internal/reflect/NoSuchPropertyException.java  |  26 +
 .../gradle/internal/reflect/PropertyAccessor.java  |  25 +
 .../gradle/internal/reflect/PropertyMutator.java   |  25 +
 .../gradle/internal/service/ServiceLocator.java    |  88 ++-
 .../service/SynchronizedServiceRegistry.java       |   3 -
 .../main/java/org/gradle/util/CollectionUtils.java |  20 +
 .../src/main/java/org/gradle/util/JavaMethod.java  |  75 ++
 .../main/java}/org/gradle/util/hash/HashUtil.java  |   0
 .../main/java}/org/gradle/util/hash/HashValue.java |   0
 .../gradle/api/internal/TransformersTest.groovy    |  24 +
 .../org/gradle/internal/LazyIterableTest.groovy    |  37 -
 .../classloader/CachingClassLoaderTest.groovy      |  78 +++
 .../DefaultClassLoaderFactoryTest.groovy           |  91 +++
 .../DefaultClassLoaderFactoryTestHelper.java       |  35 +
 .../classloader/FilteringClassLoaderTest.groovy    | 235 +++++++
 .../classloader/MultiParentClassLoaderTest.groovy  | 123 ++++
 .../classloader/MutableURLClassLoaderTest.groovy   |  39 ++
 .../concurrent/ServiceLifecycleTest.groovy         | 203 ++++++
 .../groovy/org/gradle/internal/jvm/JvmTest.groovy  | 224 ++++--
 .../gradle/internal/os/OperatingSystemTest.groovy  |  33 +
 .../internal/reflect/JavaReflectionUtilTest.groovy | 242 ++++++-
 .../gradle/internal/reflect/JavaTestSubject.java   |  99 +++
 .../internal/reflect/JavaTestSubjectSubclass.java  |  31 +
 .../internal/service/ServiceLocatorTest.groovy     | 136 +++-
 .../service/SynchronizedServiceRegistryTest.groovy |   3 -
 .../org/gradle/util/CollectionUtilsTest.groovy     |   5 +
 .../groovy/org/gradle/util/JavaMethodTest.java     |  71 ++
 .../org/gradle/util/hash/HashValueTest.groovy      |   0
 .../resources/org/gradle/util/ClassLoaderTest.txt  |   0
 .../build-comparison/build-comparison.gradle       |   6 +-
 .../gradle/CompareGradleBuilds.java                |   2 +-
 .../internal/ComparableGradleBuildExecuter.java    |   2 +-
 .../internal/DefaultGradleBuildInvocationSpec.java |   3 -
 .../gradle/internal/GradleBuildComparison.java     |   2 +-
 .../internal/GradleBuildOutcomeSetInferrer.java    |  10 +-
 .../internal/GradleBuildOutcomeSetTransformer.java |  12 +-
 .../outcome/internal/FileOutcomeIdentifier.java    |  41 ++
 .../archive/GeneratedArchiveBuildOutcome.java      |  10 +-
 .../tooling/DefaultGradleBuildOutcome.java         |  46 ++
 .../tooling/DefaultGradleFileBuildOutcome.java     |  42 ++
 .../internal/tooling/DefaultProjectOutcomes.java   |  79 +++
 .../tooling/ProjectOutcomesModelBuilder.java       |  67 ++
 ...blishArtifactToFileBuildOutcomeTransformer.java |  97 +++
 .../tooling/ToolingRegistrationAction.java         |  27 +
 ...le.configuration.project.ProjectConfigureAction |   1 +
 .../DefaultGradleBuildInvocationSpecTest.groovy    |   4 +-
 .../GradleBuildOutcomeSetInferrerTest.groovy       |   2 +-
 .../GradleBuildOutcomeSetTransformerTest.groovy    |  26 +-
 ...neratedArchiveBuildOutcomeComparatorTest.groovy |   8 +-
 ...rtifactToFileBuildOutcomeTransformerTest.groovy |  87 +++
 subprojects/build-setup/build-setup.gradle         |  25 +
 .../plugins/BuildSetupPluginIntegrationTest.groovy | 204 ++++++
 .../plugins/JavaLibrarySetupIntegrationTest.groovy |  76 ++
 .../plugins/MavenConversionIntegrationTest.groovy  | 202 ++++++
 .../plugins/WrapperPluginIntegrationTest.groovy    |  35 +
 .../plugins/fixtures/WrapperTestFixture.groovy     |  50 ++
 .../WrapperPluginAutoApplyActionIntegTest.groovy   |  99 +++
 .../enforcerplugin/pom.xml                         |   0
 .../enforcerplugin/src/main/java/Foo.java          |   0
 .../flatmultimodule/webinar-api/pom.xml            |   0
 .../src/main/java/webinar/Demoable.java            |   0
 .../flatmultimodule/webinar-impl/pom.xml           |   0
 .../src/main/java/webinar/Webinar.java             |   0
 .../src/test/java/webinar/WebinarTest.java         |   0
 .../flatmultimodule/webinar-parent/pom.xml         |   0
 .../flatmultimodule/webinar-war/pom.xml            |   0
 .../webinar-war/src/main/webapp/WEB-INF/web.xml    |   0
 .../webinar-war/src/main/webapp/index.jsp          |   0
 .../mavenExtensions/pom.xml                        |  47 ++
 .../mavenExtensions/test-core/pom.xml              |  22 +
 .../multiModule/pom.xml                            |   0
 .../multiModule/webinar-api/pom.xml                |   0
 .../src/main/java/webinar/Demoable.java            |   0
 .../multiModule/webinar-impl/pom.xml               |   0
 .../src/main/java/webinar/Webinar.java             |   0
 .../src/test/java/webinar/WebinarTest.java         |   0
 .../multiModule/webinar-war/pom.xml                |   0
 .../webinar-war/src/main/webapp/WEB-INF/web.xml    |   0
 .../webinar-war/src/main/webapp/index.jsp          |   0
 .../providedNotWar/pom.xml                         |  30 +
 .../singleModule/pom.xml                           |   0
 .../singleModule/src/main/java/Foo.java            |   0
 .../singleModule/src/test/java/FooTest.java        |   0
 .../MavenConversionIntegrationTest/testjar/pom.xml |   0
 .../testjar/src/main/java/Foo.java                 |   0
 .../testjar/src/test/java/FooTest.java             |   0
 .../org/gradle/api/tasks/wrapper/Wrapper.java      | 278 ++++++++
 .../org/gradle/api/tasks/wrapper/package-info.java |   0
 .../buildsetup/plugins/BuildSetupPlugin.groovy     |  87 +++
 .../gradle/buildsetup/plugins/WrapperPlugin.groovy |  34 +
 .../internal/BasicProjectSetupDescriptor.groovy    |  41 ++
 .../internal/BuildSetupAutoApplyAction.java        |  33 +
 .../plugins/internal/BuildSetupTypeIds.java        |  27 +
 .../JavaLibraryProjectSetupDescriptor.groovy       |  57 ++
 .../internal/PomProjectSetupDescriptor.groovy      |  51 ++
 .../internal/ProjectLayoutSetupRegistry.groovy     |  47 ++
 .../ProjectLayoutSetupRegistryFactory.groovy       |  47 ++
 .../plugins/internal/ProjectSetupDescriptor.groovy |  25 +
 .../TemplateBasedProjectSetupDescriptor.groovy     |  73 ++
 .../buildsetup/plugins/internal/TemplateValue.java |  67 ++
 .../internal/WrapperPluginAutoApplyAction.groovy   |  32 +
 .../plugins/internal/maven/Maven2Gradle.groovy     | 514 ++++++++++++++
 .../internal/maven/MavenConversionException.java   |  29 +
 .../internal/maven/MavenProjectXmlWriter.java      |  58 ++
 .../internal/maven/MavenProjectsCreator.java       |  92 +++
 .../org/gradle/buildsetup/tasks/SetupBuild.groovy  |  59 ++
 .../META-INF/gradle-plugins/build-setup.properties |   1 +
 .../META-INF/gradle-plugins/wrapper.properties     |  17 +
 ...le.configuration.project.ProjectConfigureAction |   2 +
 .../tasks/templates/Library.java.template          |  11 +
 .../tasks/templates/LibraryTest.java.template      |  15 +
 .../tasks/templates/build.gradle.template          |  32 +
 .../templates/java-library-build.gradle.template   |  30 +
 .../tasks/templates/settings.gradle.template       |  19 +
 .../org/gradle/api/tasks/wrapper/WrapperTest.java  | 161 +++++
 .../buildsetup/plugins/BuildSetupPluginSpec.groovy |  77 ++
 .../buildsetup/plugins/WrapperPluginSpec.groovy    |  36 +
 .../internal/BuildSetupAutoApplyActionSpec.groovy  |  72 ++
 .../ProjectLayoutSetupRegistryFactoryTest.groovy   |  52 ++
 .../internal/ProjectLayoutSetupRegistrySpec.groovy |  58 ++
 .../TemplateBasedProjectSetupDescriptorSpec.groovy | 105 +++
 .../plugins/internal/TemplateValueTest.groovy      |  48 ++
 .../maven/MavenProjectXmlWriterTest.groovy         |  31 +
 .../internal/maven/MavenProjectsCreatorSpec.groovy | 117 ++++
 .../gradle/buildsetup/tasks/SetupBuildSpec.groovy  |  69 ++
 subprojects/cli/cli.gradle                         |   2 +-
 .../gradle/cli/CommandLineArgumentException.java   |   2 -
 .../java/org/gradle/cli/CommandLineConverter.java  |   3 -
 .../java/org/gradle/cli/CommandLineOption.java     |  20 +-
 .../java/org/gradle/cli/CommandLineParser.java     |  22 +-
 .../java/org/gradle/cli/ParsedCommandLine.java     |   4 +
 .../org/gradle/cli/CommandLineParserTest.groovy    |  50 +-
 .../gradle/cli/ParsedCommandLineOptionSpec.groovy  |   3 -
 .../org/gradle/cli/ParsedCommandLineTest.groovy    |   3 -
 subprojects/code-quality/code-quality.gradle       |   2 +-
 .../quality/CheckstylePluginIntegrationTest.groovy |   6 +-
 .../quality/CodeNarcPluginIntegrationTest.groovy   |  18 +-
 .../CodeQualityPluginIntegrationTest.groovy        |  18 +-
 .../quality/FindBugsPluginIntegrationTest.groovy   |   2 +-
 .../quality/PmdPluginIntegrationTest.groovy        |   4 +-
 .../quality/PmdPluginVersionIntegrationTest.groovy |   4 +-
 .../api/plugins/quality/CheckstyleReports.java     |   7 +-
 .../org/gradle/api/plugins/quality/CodeNarc.groovy |  22 +-
 .../api/plugins/quality/CodeNarcExtension.groovy   |  15 +
 .../api/plugins/quality/CodeNarcPlugin.groovy      |   8 +-
 .../api/plugins/quality/CodeNarcReports.java       |  11 +-
 .../api/plugins/quality/FindBugsPlugin.groovy      |   2 +-
 .../api/plugins/quality/FindBugsReports.java       |  13 +-
 .../org/gradle/api/plugins/quality/JDepend.groovy  |   2 +-
 .../gradle/api/plugins/quality/JDependReports.java |   9 +-
 .../gradle/api/plugins/quality/PmdExtension.groovy |   1 +
 .../org/gradle/api/plugins/quality/PmdReports.java |  10 +-
 .../internal/AbstractCodeQualityPlugin.groovy      |   4 +-
 .../plugins/quality/CheckstylePluginTest.groovy    |   8 +-
 .../api/plugins/quality/CheckstyleTest.groovy      |   2 +-
 .../api/plugins/quality/CodeNarcPluginTest.groovy  |  35 +-
 .../plugins/quality/CodeQualityPluginTest.groovy   |  12 +-
 .../api/plugins/quality/FindBugsPluginTest.groovy  |   9 +-
 .../gradle/api/plugins/quality/FindBugsTest.groovy |   2 +-
 .../api/plugins/quality/JDependPluginTest.groovy   |   8 +-
 .../api/plugins/quality/PmdPluginTest.groovy       |   8 +-
 subprojects/core-impl/core-impl.gradle             |   5 +-
 .../ArtifactDependenciesIntegrationTest.groovy     |  36 +-
 ...ModuleDependenciesResolveIntegrationTest.groovy |   3 -
 .../DependencyNotationIntegrationSpec.groovy       |   3 -
 .../DependencyResolveRulesIntegrationTest.groovy   |  36 +-
 .../DetachedConfigurationsIntegrationTest.groovy   |  62 ++
 .../ExtendingConfigurationsIntegrationTest.groovy  |  60 ++
 .../resolve/ForcedModulesIntegrationTest.groovy    |   5 +-
 .../ProjectDependenciesIntegrationTest.groovy      |   5 +-
 .../ProjectDependencyResolveIntegrationTest.groovy |  41 +-
 .../ResolutionResultApiIntegrationTest.groovy      |  75 ++
 ...ResolutionStrategySamplesIntegrationTest.groovy |   5 +-
 .../ResolveCrossVersionIntegrationTest.groovy      |  20 +-
 .../ResolvedConfigurationIntegrationTest.groovy    |   6 +-
 ...VersionConflictResolutionIntegrationTest.groovy | 153 +++-
 ...actCacheReuseCrossVersionIntegrationTest.groovy |   3 -
 ...AliasedArtifactResolutionIntegrationTest.groovy |  21 +-
 ...ependencyMetadataInMemoryIntegrationTest.groovy | 197 ++++++
 .../FileSystemResolverIntegrationTest.groovy       |  12 +-
 .../custom/IvySFtpResolverIntegrationTest.groovy   |   8 +-
 .../custom/IvyUrlResolverIntegrationTest.groovy    |   6 +-
 .../IvyBrokenRemoteResolveIntegrationTest.groovy   |  33 +-
 ...angingModuleRemoteResolveIntegrationTest.groovy |   1 -
 ...IvyComponentMetadataRulesIntegrationTest.groovy | 132 ++++
 ...CustomStatusLatestVersionIntegrationTest.groovy |  62 ++
 .../ivy/IvyDescriptorResolveIntegrationTest.groovy | 140 +++-
 ...amicRevisionRemoteResolveIntegrationTest.groovy |  43 ++
 ...IvyDynamicRevisionResolveIntegrationTest.groovy | 125 +++-
 .../IvyMetadataConsistencyIntegrationTest.groovy   |  54 ++
 .../ivy/IvyModuleResolveIntegrationTest.groovy     | 209 ++++++
 .../resolve/ivy/IvyResolveIntegrationTest.groovy   | 108 +--
 .../maven/BadPomFileResolveIntegrationTest.groovy  |  50 +-
 .../LegacyMavenRepoResolveIntegrationTest.groovy   |  19 +-
 .../MavenBrokenRemoteResolveIntegrationTest.groovy |  50 ++
 ...venComponentMetadataRulesIntegrationTest.groovy |  61 ++
 .../MavenDependencyResolveIntegrationTest.groovy   |   3 +
 ...nJcenterDependencyResolveIntegrationTest.groovy |  72 ++
 .../maven/MavenLatestResolveIntegrationTest.groovy |  91 +++
 .../MavenParentPomResolveIntegrationTest.groovy    |  85 +++
 .../MavenSnapshotResolveIntegrationTest.groovy     |  13 +-
 .../canNestModules/projectWithNestedModules.gradle |   2 +-
 .../projectWithConflicts.gradle                    |   2 +-
 .../artifacts/ArtifactDependencyResolver.java      |   3 -
 .../BuildableModuleVersionPublishMetaData.java     |  28 +
 .../internal/artifacts/ConfigurationResolver.java  |   3 -
 .../artifacts/DefaultArtifactIdentifier.java       | 119 ++++
 .../artifacts/DefaultDependencyFactory.java        |   3 -
 .../DefaultDependencyManagementServices.java       | 114 +--
 .../artifacts/DefaultModuleVersionIdentifier.java  |   5 +
 .../DefaultModuleVersionPublishMetaData.java       |  52 ++
 .../artifacts/DefaultModuleVersionSelector.java    |   8 +-
 .../artifacts/DefaultProjectDependencyFactory.java |   3 -
 .../artifacts/DefaultResolvedArtifact.java         |  28 +-
 .../artifacts/DefaultResolvedDependency.java       |  17 +-
 .../artifacts/ModuleMetadataProcessor.java         |  22 +
 .../ModuleVersionIdentifierSerializer.java         |  26 +-
 .../artifacts/ModuleVersionSelectorSerializer.java |  41 ++
 .../internal/artifacts/PlexusLoggerAdapter.java    |   3 -
 .../artifacts/ResolvedConfigurationIdentifier.java |   6 +-
 .../ResolvedConfigurationIdentifierSerializer.java |  39 ++
 .../api/internal/artifacts/ResolverResults.java    |   3 -
 .../artifacts/configurations/Configurations.java   |   8 -
 .../configurations/ConfigurationsProvider.java     |   3 -
 .../configurations/DefaultConfiguration.java       |   4 +
 .../DefaultConfigurationContainer.java             |   8 +-
 .../DetachedConfigurationsProvider.java            |   3 -
 .../artifacts/dsl/DefaultArtifactHandler.groovy    |   5 +-
 .../dsl/DefaultComponentMetadataHandler.java       |  43 ++
 .../dsl/ModuleVersionSelectorParsers.java          |   3 -
 .../artifacts/dsl/ParsedModuleStringNotation.java  |   3 -
 .../dsl/PublishArtifactNotationParserFactory.java  |   3 -
 .../ivyservice/BuildableArtifactResolveResult.java |   6 +-
 .../DefaultBuildableArtifactResolveResult.java     |   4 +-
 ...DefaultBuildableModuleVersionResolveResult.java |   6 +-
 .../ivyservice/DefaultCacheLockingManager.java     |   3 +-
 .../DefaultDependencyResolveDetails.java           |   3 -
 .../ivyservice/DefaultIvyContextManager.java       | 104 +++
 .../ivyservice/DefaultIvyDependencyPublisher.java  | 136 +---
 .../artifacts/ivyservice/DefaultIvyFactory.java    |  38 -
 .../ivyservice/DefaultLenientConfiguration.java    | 113 +--
 .../ivyservice/DefaultResolvedConfiguration.java   |  13 +-
 .../ivyservice/DefaultSettingsConverter.java       |  70 --
 .../ivyservice/DependencyToModuleResolver.java     |  28 -
 .../DependencyToModuleVersionResolver.java         |  28 +
 .../ErrorHandlingArtifactDependencyResolver.java   | 108 ++-
 .../ivyservice/IvyBackedArtifactPublisher.java     |  66 +-
 .../artifacts/ivyservice/IvyContextManager.java    |  39 ++
 .../ivyservice/IvyDependencyPublisher.java         |  14 +-
 .../internal/artifacts/ivyservice/IvyFactory.java  |  26 -
 .../artifacts/ivyservice/IvyLoggingAdaper.java     |  18 +-
 .../ivyservice/IvyModuleDescriptorWriter.java      |   0
 .../IvyResolverBackedModuleVersionPublisher.java   |  66 ++
 .../artifacts/ivyservice/IvySettingsFactory.java   |  40 --
 .../api/internal/artifacts/ivyservice/IvyUtil.java |   3 -
 .../ivyservice/IvyXmlModuleDescriptorWriter.java   |   7 +-
 .../ivyservice/ModuleDescriptorConverter.java      |  26 +
 .../ivyservice/ModuleToModuleVersionResolver.java  |  29 +
 .../ivyservice/ModuleVersionResolveException.java  |  10 +-
 .../ivyservice/ResolvedArtifactFactory.java        |  28 +-
 .../ivyservice/ResolvedConfigurationBuilder.java   |  31 -
 .../artifacts/ivyservice/SettingsConverter.java    |  34 -
 ...cuitEmptyConfigsArtifactDependencyResolver.java |   4 +-
 .../SubstitutedModuleVersionIdResolveResult.java   |   3 -
 .../clientmodule/ClientModuleResolver.java         |  13 +-
 .../DefaultCachedModuleResolution.java             |  11 +-
 .../DefaultResolvedModuleVersion.java              |  10 +-
 .../dynamicversions/ModuleResolutionCache.java     |   6 +-
 .../SingleFileBackedModuleResolutionCache.java     |  75 +-
 .../AbstractDependencyResolverAdapter.java         |  55 --
 .../ivyresolve/ArtifactNotFoundException.java      |   6 +-
 .../ivyresolve/ArtifactResolveException.java       |  26 +-
 .../ivyresolve/BuildableModuleVersionMetaData.java |  78 ---
 ...uildableModuleVersionMetaDataResolveResult.java |  83 +++
 .../CacheLockingModuleVersionRepository.java       |   6 +-
 .../ivyresolve/CachingModuleVersionRepository.java |  64 +-
 .../ivyresolve/ConfigurationMetaData.java          |  42 ++
 .../ConfiguredModuleVersionRepository.java         |  23 +
 .../DefaultBuildableModuleVersionMetaData.java     | 137 ----
 ...uildableModuleVersionMetaDataResolveResult.java | 286 ++++++++
 .../ivyresolve/DefaultDependencyMetaData.java      |   8 +-
 .../ivyservice/ivyresolve/DefaultIvyAdapter.java   |  25 +-
 .../ivyresolve/DelegatingDependencyResolver.java   | 149 ----
 .../ivyservice/ivyresolve/DependencyMetaData.java  |   2 +
 .../ivyresolve/DependencyResolverIdentifier.java   |  42 +-
 .../ExternalResourceResolverAdapter.java           |  47 --
 .../ivyservice/ivyresolve/IvyAdapter.java          |  11 +-
 .../IvyAwareModuleVersionRepository.java           |   6 +-
 .../ivyservice/ivyresolve/IvyContextualiser.java   |  36 +-
 .../ivyresolve/IvyDependencyResolverAdapter.java   |  57 +-
 .../IvyDynamicResolveModuleVersionRepository.java  |  16 +-
 .../ivyresolve/LazyDependencyToModuleResolver.java |   8 +-
 .../LocalAwareModuleVersionRepository.java         |   6 +-
 .../ivyresolve/LocalModuleVersionRepository.java   |   8 +-
 .../ivyresolve/LoopbackDependencyResolver.java     |  94 ++-
 .../ivyservice/ivyresolve/ModuleSource.java        |   8 +-
 .../ivyresolve/ModuleVersionMetaData.java          |  10 +-
 .../ivyresolve/ModuleVersionRepository.java        |  14 +-
 .../ivyresolve/MutableModuleVersionMetaData.java   |  24 +
 .../ivyservice/ivyresolve/ResolveIvyFactory.java   |  83 ++-
 .../ivyresolve/RestrictedDependencyResolver.java   |  36 -
 .../StartParameterResolutionOverride.java          |   6 +-
 .../ivyservice/ivyresolve/UserResolverChain.java   |  43 +-
 .../ivyservice/ivyresolve/VersionInfo.java         |  39 ++
 .../artifacts/ivyservice/ivyresolve/Versioned.java |  20 +
 .../memcache/CachedModuleVersionResult.java        |  64 ++
 .../ivyresolve/memcache/CachedRepository.java      |  65 ++
 .../memcache/DependencyMetadataCache.java          |  84 +++
 .../memcache/DependencyMetadataCacheStats.java     |  29 +
 .../memcache/InMemoryDependencyMetadataCache.java  |  62 ++
 .../parser/AbstractDescriptorParseContext.java     |  66 ++
 .../parser/AbstractModuleDescriptorParser.java     |  48 ++
 .../ivyresolve/parser/DefaultMetaDataParser.java   |  34 +
 .../ivyresolve/parser/DescriptorParseContext.java  |  36 +
 .../parser/DisconnectedDescriptorParseContext.java |  46 ++
 .../parser/DisconnectedParserSettings.java         | 110 ---
 .../DownloadedIvyModuleDescriptorParser.java       |  11 +-
 .../parser/GradlePomModuleDescriptorBuilder.java   |  67 +-
 .../parser/GradlePomModuleDescriptorParser.java    | 364 ++++------
 .../parser/IvyXmlModuleDescriptorParser.java       | 482 ++++++++++---
 .../ivyresolve/parser/MetaDataParseException.java  |  32 +
 .../ivyresolve/parser/MetaDataParser.java          |  23 +
 .../ivyresolve/parser/ModuleDescriptorAdapter.java |  87 +++
 .../ivyresolve/parser/ModuleDescriptorParser.java  |  31 +
 .../parser/ModuleScopedParserSettings.java         | 102 ---
 .../ivyresolve/parser/ParserRegistry.java          |   5 +-
 .../ivyservice/ivyresolve/parser/PomReader.java    | 577 +++++++++++++++
 .../ivyresolve/strategy/ChainVersionMatcher.java   |  64 ++
 .../ivyresolve/strategy/ExactVersionMatcher.java   | 113 +++
 .../ivyresolve/strategy/LatestStrategy.java        |  42 ++
 .../ivyresolve/strategy/LatestVersionMatcher.java  |  47 ++
 .../ivyresolve/strategy/LatestVersionStrategy.java |  66 ++
 .../ivyresolve/strategy/ResolverStrategy.java      |  62 ++
 .../ivyresolve/strategy/SubVersionMatcher.java     |  59 ++
 .../ivyresolve/strategy/VersionMatcher.java        |  68 ++
 .../ivyresolve/strategy/VersionRangeMatcher.java   | 180 +++++
 .../CachedModuleDescriptorParseContext.java        |  53 ++
 .../modulecache/DefaultCachedModuleDescriptor.java |   7 +-
 .../modulecache/DefaultModuleDescriptorCache.java  |  70 +-
 .../modulecache/ModuleDescriptorCache.java         |   4 +-
 .../modulecache/ModuleDescriptorStore.java         |  35 +-
 .../ArtifactsExtraAttributesStrategy.java          |  27 -
 .../ArtifactsToModuleDescriptorConverter.java      |   7 +-
 .../ConfigurationsToModuleDescriptorConverter.java |   3 -
 ...efaultArtifactsToModuleDescriptorConverter.java |  29 +-
 ...tConfigurationsToModuleDescriptorConverter.java |   3 -
 .../DefaultExcludeRuleConverter.java               |   3 -
 .../DefaultModuleDescriptorFactory.java            |  24 +-
 .../moduleconverter/ExcludeRuleConverter.java      |   3 -
 .../moduleconverter/ModuleDescriptorFactory.java   |   3 -
 .../PublishModuleDescriptorConverter.java          |  25 +-
 .../ResolveModuleDescriptorConverter.java          |  23 +-
 .../AbstractIvyDependencyDescriptorFactory.java    |   3 -
 ...ClientModuleIvyDependencyDescriptorFactory.java |   3 -
 ...ultDependenciesToModuleDescriptorConverter.java |   3 -
 .../DefaultDependencyDescriptorFactory.java        |   3 -
 ...aultModuleDescriptorFactoryForClientModule.java |   5 +-
 .../DependenciesToModuleDescriptorConverter.java   |   3 -
 .../dependencies/DependencyDescriptorFactory.java  |   3 -
 ...ternalModuleIvyDependencyDescriptorFactory.java |   3 -
 .../ModuleDescriptorFactoryForClientModule.java    |   3 -
 .../ProjectIvyDependencyDescriptorFactory.java     |   9 +-
 .../DefaultProjectModuleRegistry.java              |  23 +-
 .../projectmodule/ProjectDependencyResolver.java   |  55 +-
 .../projectmodule/ProjectModuleRegistry.java       |   4 +-
 .../DefaultResolutionStrategy.java                 |   3 -
 .../LatestConflictResolution.java                  |   2 -
 .../ModuleForcingResolveRule.java                  |   3 -
 .../StrictConflictResolution.java                  |   2 -
 .../resolveengine/DefaultDependencyResolver.java   |  72 +-
 .../DefaultDependencyToConfigurationResolver.java  |  78 +++
 .../resolveengine/DependencyGraphBuilder.java      | 548 +++++++--------
 .../DependencyToConfigurationResolver.java         |  30 +
 .../LatestModuleConflictResolver.java              |  19 +-
 .../resolveengine/ModuleConflictResolver.java      |   2 +-
 .../resolveengine/ModuleRevisionResolveState.java  |   5 +-
 .../resolveengine/StrictConflictResolver.java      |   2 +-
 .../VersionSelectionReasonResolver.java            |   7 +-
 .../DefaultResolvedConfigurationBuilder.java       | 134 ++++
 .../DefaultTransientConfigurationResults.java      |  45 ++
 .../oldresult/ResolvedConfigurationBuilder.java    |  43 ++
 .../oldresult/ResolvedConfigurationResults.java    |  32 +
 .../oldresult/ResolvedContentsMapping.java         |  28 +
 .../oldresult/TransientConfigurationResults.java   |  32 +
 .../oldresult/TransientResultsStore.java           | 185 +++++
 .../result/CachingDependencyResultFactory.java     |   6 +-
 .../result/DefaultInternalDependencyResult.java    |  59 ++
 .../result/DefaultModuleVersionSelection.java      |  38 +
 .../result/InternalDependencyResult.java           |   3 -
 .../result/InternalDependencyResultSerializer.java |  61 ++
 .../result/ModuleVersionSelection.java             |   3 -
 .../ModuleVersionSelectionReasonSerializer.java    |  59 ++
 .../result/ModuleVersionSelectionSerializer.java   |  43 ++
 .../result/ResolutionResultBuilder.java            |  24 +-
 .../result/ResolvedConfigurationListener.java      |   6 +-
 .../result/StreamingResolutionResultBuilder.java   | 196 ++++++
 .../result/VersionSelectionReasons.java            |   3 -
 .../resolveengine/store/CachedStoreFactory.java    | 101 +++
 .../resolveengine/store/DefaultBinaryStore.java    | 138 ++++
 .../resolveengine/store/EncodedWriteAction.java    |  31 +
 .../store/ResolutionResultsStoreFactory.java       | 103 +++
 .../DefaultLocalMavenRepositoryLocator.java        |   7 +-
 .../mvnsettings/DefaultMavenFileLocations.java     |   3 -
 .../mvnsettings/DefaultMavenSettingsProvider.java  |   3 -
 .../repositories/DefaultBaseRepositoryFactory.java |  47 +-
 .../DefaultFlatDirArtifactRepository.java          |  45 +-
 .../repositories/DefaultIvyArtifactRepository.java |  37 +-
 .../DefaultMavenArtifactRepository.java            |  27 +-
 .../FixedResolverArtifactRepository.java           |  10 +-
 .../repositories/LegacyDependencyResolver.java     |  44 +-
 .../repositories/LegacyMavenResolver.java          |   5 +-
 .../repositories/ResolutionAwareRepository.java    |   4 +-
 .../AbstractRepositoryArtifactCache.java           |  20 +
 .../AbstractRepositoryCacheManager.java            |  86 ---
 .../DownloadingRepositoryArtifactCache.java        |  73 ++
 .../DownloadingRepositoryCacheManager.java         | 164 -----
 .../EnhancedArtifactDownloadReport.java            |  38 -
 .../LocalFileRepositoryArtifactCache.java          |  43 ++
 .../LocalFileRepositoryCacheManager.java           |  88 ---
 .../cachemanager/RepositoryArtifactCache.java      |  45 ++
 .../repositories/layout/MavenRepositoryLayout.java |   2 +-
 .../legacy/AbstractRepositoryCacheManager.java     |  85 +++
 .../legacy/CustomIvyResolverRepositoryFactory.java |  42 ++
 .../legacy/DownloadingRepositoryCacheManager.java  | 155 +++++
 .../legacy/EnhancedArtifactDownloadReport.java     |  38 +
 .../LegacyDependencyResolverRepositoryFactory.java |  24 +
 .../legacy/LegacyResolverParserSettings.java       |  98 +++
 .../legacy/LocalFileRepositoryCacheManager.java    |  88 +++
 .../repositories/resolver/AbstractVersionList.java |  40 +-
 .../resolver/ChainedVersionLister.java             |  16 +-
 .../resolver/ComponentMetadataDetailsAdapter.java  |  58 ++
 .../repositories/resolver/DefaultVersionList.java  |  21 +-
 .../resolver/ExternalResourceResolver.java         | 771 +++++++--------------
 ...rnalResourceResolverDescriptorParseContext.java |  58 ++
 .../repositories/resolver/IvyResolver.java         |  15 +-
 .../repositories/resolver/MavenMetadataLoader.java |  37 +-
 .../repositories/resolver/MavenResolver.java       |  45 +-
 .../repositories/resolver/MavenVersionLister.java  |   8 +-
 .../resolver/PatternBasedResolver.java             |   4 +-
 .../resolver/ResourceVersionLister.java            |   8 +-
 .../repositories/resolver/VersionList.java         |  39 +-
 .../repositories/resolver/VersionLister.java       |   6 +-
 .../transport/RepositoryTransportFactory.java      |  10 +-
 .../artifacts/result/AbstractDependencyResult.java |   3 -
 .../artifacts/result/DefaultResolutionResult.java  |  20 +-
 .../result/DefaultResolvedDependencyResult.java    |   3 -
 .../result/DefaultResolvedModuleVersionResult.java |   8 -
 .../result/DefaultUnresolvedDependencyResult.java  |   3 -
 .../externalresource/AbstractExternalResource.java |  24 +-
 .../DefaultLocallyAvailableExternalResource.java   |  48 ++
 .../externalresource/ExternalResource.java         |  60 +-
 .../LocallyAvailableExternalResource.java          |  34 +-
 .../externalresource/UrlExternalResource.java      |  65 ++
 .../ArtifactAtRepositoryCachedArtifactIndex.java   |  67 +-
 .../ivy/ArtifactAtRepositoryKey.java               |  31 +-
 .../CompositeLocallyAvailableResourceFinder.java   |   1 +
 .../local/DefaultLocallyAvailableResource.java     |  65 --
 .../LazyLocallyAvailableResourceCandidates.java    |   2 +
 .../local/LocallyAvailableResource.java            |  31 -
 .../local/LocallyAvailableResourceCandidates.java  |   1 +
 .../local/LocallyAvailableResourceFinder.java      |   2 +-
 ...leResourceFinderSearchableFileStoreAdapter.java |  10 +-
 .../ivy/LocallyAvailableResourceFinderFactory.java |  11 +-
 .../DefaultCacheAwareExternalResourceAccessor.java |   6 +-
 .../ProgressLoggingExternalResourceAccessor.java   |  15 +-
 .../transport/file/FileResourceConnector.java      |  11 +-
 .../transport/file/FileTransport.java              |   6 +-
 .../http/ApacheDirectoryListingParser.java         |   9 +-
 .../transport/http/HttpResourceLister.java         |  33 +-
 .../transport/http/HttpTransport.java              |   6 +-
 .../ClientModuleNotationParserFactory.java         |   3 -
 .../notations/DependencyMapNotationParser.java     |   5 +-
 .../notations/DependencyNotationParser.java        |   3 -
 .../notations/DependencyProjectNotationParser.java |   3 -
 .../notations/DependencyStringNotationParser.java  |   3 -
 .../notations/ProjectDependencyFactory.java        |   3 -
 .../gradle/api/artifacts/ArtifactsTestUtils.java   |  62 --
 .../artifacts/DefaultArtifactIdentifierTest.groovy |  46 ++
 .../DefaultDependencyManagementServicesTest.groovy |   1 +
 .../DefaultModuleVersionSelectorTest.groovy        |  16 +-
 .../artifacts/DefaultResolvedArtifactTest.groovy   |  29 +-
 .../artifacts/DefaultResolvedDependencySpec.groovy |   2 +-
 .../artifacts/DefaultResolvedDependencyTest.java   |  74 +-
 .../ModuleVersionSelectorSerializerTest.groovy     |  33 +
 ...vedConfigurationIdentifierSerializerTest.groovy |  37 +
 .../ResolvedConfigurationIdentifierSpec.groovy     |  14 +-
 .../internal/artifacts/ResolverResultsSpec.groovy  |   3 -
 .../configurations/ConfigurationsTest.java         |   8 -
 .../DefaultConfigurationContainerSpec.groovy       |   2 +-
 .../DefaultConfigurationContainerTest.groovy       |  33 +-
 .../configurations/DefaultConfigurationTest.java   |  12 +-
 .../internal/artifacts/dsl/ArtifactFileTest.groovy |   3 -
 .../dsl/DefaultArtifactHandlerTest.groovy          |   7 +-
 .../dsl/ModuleVersionSelectorParsersTest.groovy    |   3 -
 ...PublishArtifactNotationParserFactoryTest.groovy |   3 -
 ...efaultBuildableArtifactResolveResultTest.groovy |   5 +-
 .../DefaultDependencyResolveDetailsSpec.groovy     |   5 +-
 .../ivyservice/DefaultIvyContextManagerTest.groovy | 225 ++++++
 .../ivyservice/DefaultIvyFactoryTest.groovy        |  40 --
 .../ivyservice/DefaultSettingsConverterTest.groovy | 114 ---
 .../DefaultUnresolvedDependencySpec.groovy         |   3 -
 ...orHandlingArtifactDependencyResolverTest.groovy |  61 +-
 .../ivyservice/IvyBackedArtifactPublisherTest.java | 173 -----
 .../ivyservice/IvySettingsFactoryTest.groovy       |  37 -
 .../artifacts/ivyservice/IvyUtilTest.groovy        |   4 +-
 .../ModuleVersionNotFoundExceptionTest.groovy      |   6 +-
 .../ModuleVersionResolveExceptionTest.groovy       |   6 +-
 .../ivyservice/ResolvedArtifactFactoryTest.groovy  |  23 +-
 .../clientmodule/ClientModuleResolverTest.groovy   |   9 +-
 .../CachingModuleVersionRepositoryTest.groovy      |  46 +-
 ...leModuleVersionMetaDataResolveResultTest.groovy | 428 ++++++++++++
 ...efaultBuildableModuleVersionMetaDataTest.groovy | 244 -------
 .../DependencyResolverIdentifierTest.groovy        |  15 +-
 ...ynamicResolveModuleVersionRepositoryTest.groovy |  10 +-
 .../LazyDependencyToModuleResolverTest.groovy      |   9 +-
 .../ivyresolve/UserResolverChainTest.groovy        |  26 +-
 .../memcache/CachedModuleVersionResultTest.groovy  |  85 +++
 .../memcache/CachedRepositoryTest.groovy           | 117 ++++
 .../memcache/DependencyMetadataCacheTest.groovy    | 126 ++++
 .../InMemoryDependencyMetadataCacheTest.groovy     |  76 ++
 .../DownloadedIvyModuleDescriptorParserTest.groovy |  11 +-
 .../GradlePomModuleDescriptorParserTest.groovy     |  81 ++-
 .../parser/IvyXmlModuleDescriptorParserTest.groovy | 245 ++++++-
 .../strategy/ChainVersionMatcherTest.groovy        | 129 ++++
 .../strategy/ExactVersionMatcherTest.groovy        | 181 +++++
 .../strategy/LatestVersionMatcherTest.groovy       |  83 +++
 .../strategy/LatestVersionStrategyTest.groovy      |  78 +++
 .../strategy/SubVersionMatcherTest.groovy          |  84 +++
 .../strategy/VersionRangeMatcherTest.groovy        | 208 ++++++
 .../modulecache/ModuleDescriptorStoreTest.groovy   |  10 +-
 ...ltArtifactsToModuleDescriptorConverterTest.java |  63 +-
 ...figurationsToModuleDescriptorConverterTest.java |  14 +-
 .../DefaultExcludeRuleConverterTest.java           |   3 -
 .../DefaultModuleDescriptorFactoryTest.groovy      |  38 +-
 .../moduleconverter/IvyConverterTestUtil.java      |   3 -
 .../PublishModuleDescriptorConverterTest.groovy    |  20 +-
 .../ResolveModuleDescriptorConverterTest.groovy    |  19 +-
 ...actDependencyDescriptorFactoryInternalTest.java |   7 +-
 ...lientModuleDependencyDescriptorFactoryTest.java |   3 -
 ...ependenciesToModuleDescriptorConverterTest.java |   7 +-
 ...ModuleDescriptorFactoryForClientModuleTest.java |   3 -
 ...ernalModuleDependencyDescriptorFactoryTest.java |   3 -
 .../ProjectDependencyDescriptorFactoryTest.groovy  |  64 ++
 .../ProjectDependencyDescriptorFactoryTest.java    |  65 --
 ...eflectiveDependencyDescriptorFactoryTest.groovy |   5 +-
 .../ProjectDependencyResolverTest.groovy           |  39 +-
 .../DefaultResolutionStrategySpec.groovy           |   3 -
 .../ModuleForcingResolveRuleSpec.groovy            |   3 -
 .../DependencyGraphBuilderTest.groovy              | 192 ++---
 .../VersionSelectionReasonResolverTest.groovy      |  10 +-
 .../CachingDependencyResultFactoryTest.groovy      |   7 +-
 .../resolveengine/result/DummyBinaryStore.groovy   |  48 ++
 .../resolveengine/result/DummyStore.groovy         |  24 +
 .../InternalDependencyResultSerializerTest.groovy  |  80 +++
 ...duleVersionSelectionReasonSerializerTest.groovy |  54 ++
 .../ModuleVersionSelectionSerializerTest.groovy    |  37 +
 .../result/ResolutionResultBuilderSpec.groovy      |  58 +-
 .../result/ResolutionResultPrinter.groovy          |  47 ++
 .../StreamingResolutionResultBuilderTest.groovy    | 137 ++++
 .../result/VersionSelectionReasonsTest.groovy      |   3 -
 .../store/CachedStoreFactoryTest.groovy            |  40 ++
 .../store/DefaultBinaryStoreTest.groovy            |  82 +++
 .../store/ResolutionResultsStoreFactoryTest.groovy |  88 +++
 .../DefaultLocalMavenRepositoryLocatorTest.groovy  |   4 +-
 .../DefaultBaseRepositoryFactoryTest.groovy        | 165 +++--
 .../DefaultFlatDirArtifactRepositoryTest.groovy    |  32 +-
 .../DefaultIvyArtifactRepositoryTest.groovy        |  78 ++-
 .../DefaultMavenArtifactRepositoryTest.groovy      |  54 +-
 .../DownloadingRepositoryCacheManagerTest.groovy   |  71 --
 .../DownloadingRepositoryCacheManagerTest.groovy   |  66 ++
 .../resolver/ChainedVersionListerTest.groovy       |  38 +-
 .../resolver/ExternalResourceResolverTest.groovy   |  63 +-
 .../repositories/resolver/MavenResolverTest.groovy |   6 +-
 .../resolver/MavenVersionListerTest.groovy         |  65 +-
 .../resolver/ResourceVersionListerTest.groovy      |  47 +-
 .../DefaultArtifactResolutionCacheTest.groovy      |  80 ---
 .../result/DefaultResolutionResultTest.groovy      |  16 +-
 .../DefaultResolvedModuleVersionResultSpec.groovy  |   4 -
 .../CachedExternalResourceAdapterTest.groovy       |  83 ---
 .../CachedExternalResourceAdapterTest.groovy       |  82 +++
 .../DefaultArtifactResolutionCacheTest.groovy      |  79 +++
 ...ifactAtRepositoryCachedArtifactIndexTest.groovy |  10 +-
 ...positeLocallyAvailableResourceFinderTest.groovy |   1 +
 .../DefaultLocallyAvailableResourceTest.groovy     |  66 --
 ...ltCacheAwareExternalResourceAccessorTest.groovy |   6 +-
 ...gressLoggingExternalResourceAccessorTest.groovy |  15 +-
 .../http/ApacheDirectoryListingParserTest.groovy   |  16 +-
 .../transport/http/HttpResourceListerTest.groovy   |   3 +-
 .../DependencyMapNotationParserTest.groovy         |   8 +-
 .../notations/DependencyNotationParserTest.groovy  |   3 -
 .../DependencyStringNotationParserTest.groovy      |  12 +-
 .../notations/ProjectDependencyFactoryTest.groovy  |   3 -
 .../ivyresolve/parser/test-bad-confs.xml           |  27 -
 .../ivyresolve/parser/test-cyclic-confs1.xml       |  28 -
 .../ivyresolve/parser/test-empty-dependencies.xml  |  28 -
 .../ivyservice/ivyresolve/parser/test-full.xml     |   4 +-
 .../result/ResolutionResultDataBuilder.groovy      |   5 +-
 subprojects/core/core.gradle                       |   6 +-
 .../DeprecationHandlingIntegrationTest.groovy      | 125 ++++
 .../org/gradle/api/ApplyPluginIntegSpec.groovy     |  84 +++
 .../api/BuildScriptErrorIntegrationTest.groovy     |  98 +++
 .../ConfigurationOnDemandIntegrationTest.groovy    |  95 ++-
 .../api/CrossProcessFileLockIntegrationTest.groovy |  71 ++
 ...rredConfigurableExtensionIntegrationTest.groovy | 174 +++++
 .../api/ExternalScriptErrorIntegrationTest.groovy  |  87 +++
 .../gradle/api/FinalizerTaskIntegrationTest.groovy | 248 +++++++
 .../gradle/api/GradlePluginIntegrationTest.groovy  |  97 +++
 .../api/InitScriptErrorIntegrationTest.groovy      |  58 ++
 .../org/gradle/api/ProfilingIntegrationTest.groovy |  46 ++
 .../api/ProjectConfigurationIntegrationTest.groovy |   3 -
 ...ojectConfigureEventsErrorIntegrationTest.groovy | 114 +++
 .../api/SettingsPluginIntegrationSpec.groovy       |  83 +++
 .../api/SettingsScriptErrorIntegrationTest.groovy  |  41 ++
 .../api/dsl/ConcurrentClassDecorationSpec.groovy   |  48 ++
 .../gradle/api/tasks/ArchiveIntegrationTest.groovy |  80 ++-
 .../api/tasks/CopyTaskIntegrationSpec.groovy       |  89 +++
 .../api/tasks/CopyTaskIntegrationTest.groovy       |  74 ++
 ...kCommandLineConfigurationIntegrationSpec.groovy | 249 +++++++
 .../api/tasks/bundling/ZipIntegrationTest.groovy   | 100 +++
 .../internal/PathLimitationIntegTest.groovy        | 277 ++++++++
 .../internal/WorkerProcessIntegrationTest.java     |   3 +-
 .../src/main/groovy/org/gradle/BuildListener.java  |   4 +-
 .../src/main/groovy/org/gradle/CacheUsage.java     |   1 -
 .../src/main/groovy/org/gradle/GradleLauncher.java |  45 +-
 .../src/main/groovy/org/gradle/StartParameter.java |  57 +-
 .../groovy/org/gradle/TaskExecutionLogger.java     |   2 +-
 .../org/gradle/api/CircularReferenceException.java |   2 -
 .../main/groovy/org/gradle/api/DefaultTask.java    |   2 -
 ...ExtensiblePolymorphicDomainObjectContainer.java |  28 +
 .../org/gradle/api/GradleScriptException.java      |   2 -
 .../org/gradle/api/IllegalDependencyNotation.java  |   2 -
 .../IllegalOperationAtExecutionTimeException.java  |   3 +-
 .../org/gradle/api/InvalidUserDataException.java   |   2 -
 .../gradle/api/NamedDomainObjectCollection.java    |  14 +-
 .../org/gradle/api/NamedDomainObjectContainer.java |  10 +
 .../main/groovy/org/gradle/api/NonExtensible.java  |  31 +
 .../src/main/groovy/org/gradle/api/Plugin.java     |   3 +-
 .../src/main/groovy/org/gradle/api/Project.java    |  11 +-
 .../gradle/api/ProjectConfigurationException.java  |  29 +
 .../org/gradle/api/ProjectEvaluationListener.java  |   2 -
 .../main/groovy/org/gradle/api/ProjectState.java   |   3 +
 .../core/src/main/groovy/org/gradle/api/Rule.java  |   2 -
 .../core/src/main/groovy/org/gradle/api/Task.java  | 109 ++-
 .../org/gradle/api/UnknownProjectException.java    |   2 -
 .../org/gradle/api/UnknownTaskException.java       |   2 -
 .../main/groovy/org/gradle/api/XmlProvider.java    |   2 -
 .../gradle/api/artifacts/ArtifactIdentifier.java   |  12 +-
 .../api/artifacts/ArtifactRepositoryContainer.java |  42 +-
 .../org/gradle/api/artifacts/ClientModule.java     |   2 -
 .../api/artifacts/ComponentMetadataDetails.java    |  86 +++
 .../api/artifacts/ConfigurationContainer.java      |  14 +-
 .../org/gradle/api/artifacts/Dependency.java       |   5 +-
 .../gradle/api/artifacts/DependencyArtifact.java   |   6 +-
 .../org/gradle/api/artifacts/ExcludeRule.java      |   4 +-
 .../gradle/api/artifacts/ExcludeRuleContainer.java |   2 -
 .../gradle/api/artifacts/ExternalDependency.java   |   2 -
 .../api/artifacts/ExternalModuleDependency.java    |   2 -
 .../groovy/org/gradle/api/artifacts/Module.java    |   2 -
 .../gradle/api/artifacts/ProjectDependency.java    |   4 +-
 .../org/gradle/api/artifacts/PublishArtifact.java  |   6 +-
 .../org/gradle/api/artifacts/ResolveException.java |   2 -
 .../org/gradle/api/artifacts/ResolvedArtifact.java |   2 -
 .../gradle/api/artifacts/ResolvedDependency.java   |   2 -
 .../api/artifacts/UnknownRepositoryException.java  |   2 -
 .../gradle/api/artifacts/dsl/ArtifactHandler.java  |   2 -
 .../artifacts/dsl/ComponentMetadataHandler.java    |  54 ++
 .../api/artifacts/dsl/DependencyHandler.java       |  35 +-
 .../api/artifacts/dsl/RepositoryHandler.java       |  71 +-
 .../repositories/IvyArtifactRepository.java        |   2 +-
 .../gradle/api/component/SoftwareComponent.java    |   3 +-
 .../org/gradle/api/dsl/ConventionProperty.java     |   2 -
 .../gradle/api/execution/TaskExecutionAdapter.java |   2 +-
 .../groovy/org/gradle/api/file/CopySourceSpec.java |   2 -
 .../main/groovy/org/gradle/api/file/CopySpec.java  |  53 +-
 .../groovy/org/gradle/api/file/DeleteAction.java   |   2 -
 .../api/file/DuplicateFileCopyingException.java    |  35 +
 .../org/gradle/api/file/DuplicatesStrategy.java    |  59 ++
 .../org/gradle/api/file/EmptyFileVisitor.java      |   2 -
 .../org/gradle/api/file/FileCopyDetails.java       |  24 +
 .../main/groovy/org/gradle/api/file/FileTree.java  |   2 +-
 .../groovy/org/gradle/api/file/FileVisitor.java    |   2 -
 .../groovy/org/gradle/api/file/RelativePath.java   |   2 -
 .../api/initialization/ProjectDescriptor.java      |   2 -
 .../org/gradle/api/initialization/Settings.java    |   5 +-
 .../api/internal/AbstractClassGenerator.java       |  12 +
 .../AbstractNamedDomainObjectContainer.java        |  12 +-
 .../AbstractPolymorphicDomainObjectContainer.java  |   8 +-
 .../org/gradle/api/internal/AbstractTask.java      |  65 +-
 .../api/internal/AsmBackedClassGenerator.java      | 177 +++--
 .../org/gradle/api/internal/BeanDynamicObject.java |  60 +-
 .../api/internal/CachingDirectedGraphWalker.java   | 176 -----
 .../internal/ClassGeneratorBackedInstantiator.java |   2 +-
 .../api/internal/CompositeDomainObjectSet.java     |   8 +-
 .../gradle/api/internal/ConventionAwareHelper.java |  17 +-
 .../org/gradle/api/internal/ConventionTask.java    |   3 -
 .../DefaultNamedDomainObjectCollection.java        |   8 +
 .../DefaultPolymorphicDomainObjectContainer.java   |  45 +-
 .../internal/DependencyInjectingInstantiator.java  |   2 +-
 .../org/gradle/api/internal/DirectedGraph.java     |  26 -
 .../api/internal/DirectedGraphWithEdgeValues.java  |  25 -
 .../gradle/api/internal/DocumentationRegistry.java |  29 +-
 .../org/gradle/api/internal/GradleInternal.java    |  14 +-
 .../org/gradle/api/internal/GraphAggregator.java   |  90 ---
 .../org/gradle/api/internal/IConventionAware.java  |   2 -
 ...amedDomainObjectContainerConfigureDelegate.java |   6 +-
 ...phicDomainObjectContainerConfigureDelegate.java |  14 +-
 .../org/gradle/api/internal/SettingsInternal.java  |   4 +-
 .../org/gradle/api/internal/TaskInternal.java      |   8 +-
 .../gradle/api/internal/TaskOutputsInternal.java   |   1 +
 .../artifacts/ArtifactPublicationServices.java     |   7 -
 .../api/internal/artifacts/ArtifactPublisher.java  |   3 -
 .../internal/artifacts/BaseRepositoryFactory.java  |   2 +
 .../artifacts/CachingDependencyResolveContext.java |   6 +-
 .../artifacts/DefaultArtifactIdentifier.java       |  55 --
 .../DefaultArtifactRepositoryContainer.java        |   8 +-
 .../api/internal/artifacts/DefaultExcludeRule.java |   5 -
 .../artifacts/DefaultExcludeRuleContainer.java     |   3 -
 .../api/internal/artifacts/DefaultModule.java      |   3 -
 .../artifacts/DependencyResolutionServices.java    |   3 +
 .../DependencyResolveDetailsInternal.java          |   3 -
 .../artifacts/ExcludeRuleNotationParser.java       |   3 -
 .../artifacts/ModuleVersionPublishMetaData.java    |  32 +
 .../internal/artifacts/ModuleVersionPublisher.java |  27 +
 .../artifacts/ModuleVersionSelectorStrictSpec.java |   3 -
 .../configurations/ConfigurationInternal.java      |   2 +
 .../configurations/DependencyMetaDataProvider.java |   3 -
 .../artifacts/dependencies/AbstractDependency.java |   5 +-
 .../dependencies/DefaultClientModule.java          |   3 -
 .../DefaultExternalModuleDependency.java           |   3 -
 .../dependencies/DefaultProjectDependency.java     |   9 +-
 .../dependencies/ProjectDependencyInternal.java    |  28 +
 .../artifacts/dsl/DefaultRepositoryHandler.java    |  19 +-
 .../dependencies/DefaultDependencyHandler.groovy   |  23 +-
 .../dsl/dependencies/DependencyFactory.java        |   3 -
 .../dependencies/ModuleDescriptorDelegate.groovy   |   3 -
 .../dsl/dependencies/ModuleFactoryHelper.java      |   5 +-
 .../artifacts/dsl/dependencies/ProjectFinder.java  |   3 -
 .../ivyservice/ModuleDescriptorConverter.java      |  35 -
 .../artifacts/publish/AbstractPublishArtifact.java |   3 -
 .../artifacts/publish/ArchivePublishArtifact.java  |   3 -
 .../artifacts/publish/DefaultPublishArtifact.java  |   3 -
 .../repositories/PublicationAwareRepository.java   |   4 +-
 .../version/LatestVersionSemanticComparator.java   |  49 --
 .../org/gradle/api/internal/cache/BinaryStore.java |  41 ++
 .../org/gradle/api/internal/cache/Store.java       |  22 +
 .../CacheBackedFileSnapshotRepository.java         |  44 --
 .../CacheBackedTaskHistoryRepository.java          | 174 -----
 .../CacheLockHandlingTaskExecuter.java             |  38 -
 .../internal/changedetection/CachingHasher.java    |  78 ---
 .../changedetection/CompositeUpToDateRule.java     |  52 --
 .../changedetection/DefaultFileCacheListener.java  |  45 --
 .../changedetection/DefaultFileSnapshotter.java    | 158 -----
 .../internal/changedetection/DefaultHasher.java    |  26 -
 .../DefaultTaskArtifactStateCacheAccess.java       |  85 ---
 .../DefaultTaskArtifactStateRepository.java        | 172 -----
 ...eCacheBroadcastTaskArtifactStateRepository.java |  63 --
 .../changedetection/FileCacheListener.java         |  35 -
 .../changedetection/FileCollectionSnapshot.java    |  59 --
 .../changedetection/FileSnapshotRepository.java    |  24 -
 .../internal/changedetection/FileSnapshotter.java  |  35 -
 .../api/internal/changedetection/Hasher.java       |  22 -
 .../changedetection/InMemoryIndexedCache.java      |  65 --
 .../InputFilesChangedUpToDateRule.java             |  63 --
 .../InputPropertiesChangedUpToDateRule.java        |  55 --
 .../changedetection/MapMergeChangeListener.java    |  67 --
 .../OutputFilesChangedUpToDateRule.java            |  85 ---
 .../changedetection/OutputFilesSnapshotter.java    | 154 ----
 .../ShortCircuitTaskArtifactStateRepository.java   |  96 ---
 .../changedetection/TaskArtifactState.java         |  11 +-
 .../TaskArtifactStateCacheAccess.java              |  50 --
 .../TaskCacheLockHandlingBuildExecuter.java        |  35 -
 .../internal/changedetection/TaskExecution.java    |  67 --
 .../changedetection/TaskHistoryRepository.java     |  30 -
 .../TaskTypeChangedUpToDateRule.java               |  42 --
 .../api/internal/changedetection/UpToDateRule.java |  48 --
 .../changes/ChangesOnlyIncrementalTaskInputs.java  |  57 ++
 .../DefaultTaskArtifactStateRepository.java        | 133 ++++
 .../changes/NoHistoryArtifactState.java            |  52 ++
 .../changes/RebuildIncrementalTaskInputs.java      |  73 ++
 .../ShortCircuitTaskArtifactStateRepository.java   |  96 +++
 .../changes/StatefulIncrementalTaskInputs.java     |  49 ++
 .../rules/CachingTaskStateChanges.java             |  86 +++
 .../internal/changedetection/rules/ChangeType.java |  33 +
 .../changedetection/rules/DescriptiveChange.java   |  29 +
 .../internal/changedetection/rules/FileChange.java |  60 ++
 .../changedetection/rules/InputFileChange.java     |  31 +
 .../rules/InputFilesStateChangeRule.java           |  77 ++
 .../rules/InputPropertiesStateChangeRule.java      |  54 ++
 .../rules/NoHistoryStateChangeRule.java            |  35 +
 .../changedetection/rules/OutputFileChange.java    |  29 +
 .../rules/OutputFilesStateChangeRule.java          | 100 +++
 .../rules/SimpleTaskStateChanges.java              |  38 +
 .../rules/SummaryTaskStateChanges.java             |  76 ++
 .../changedetection/rules/TaskStateChange.java     |  21 +
 .../changedetection/rules/TaskStateChanges.java    |  25 +
 .../rules/TaskTypeStateChangeRule.java             |  43 ++
 .../changedetection/rules/TaskUpToDateState.java   |  66 ++
 .../state/CacheBackedFileSnapshotRepository.java   |  44 ++
 .../state/CacheBackedTaskHistoryRepository.java    | 273 ++++++++
 .../changedetection/state/CachingHasher.java       |  76 ++
 .../state/DefaultFileSnapshotter.java              | 185 +++++
 .../state/DefaultFileSnapshotterSerializer.java    |  68 ++
 .../changedetection/state/DefaultHasher.java       |  26 +
 .../state/DefaultTaskArtifactStateCacheAccess.java |  80 +++
 .../state/FileCollectionSnapshot.java              |  61 ++
 .../state/FileSnapshotRepository.java              |  24 +
 .../state/FileSnapshotSerializer.java              |  52 ++
 .../changedetection/state/FileSnapshotter.java     |  35 +
 .../api/internal/changedetection/state/Hasher.java |  22 +
 .../state/MapMergeChangeListener.java              |  67 ++
 .../state/OutputFilesSnapshotSerializer.java       |  61 ++
 .../state/OutputFilesSnapshotter.java              | 218 ++++++
 .../state/TaskArtifactStateCacheAccess.java        |  48 ++
 .../changedetection/state/TaskExecution.java       |  66 ++
 .../state/TaskHistoryRepository.java               |  30 +
 .../internal/classpath/DefaultModuleRegistry.java  |   2 +-
 .../api/internal/classpath/EffectiveClassPath.java |   2 +-
 .../api/internal/classpath/ManifestUtil.java       |   1 -
 .../coerce/MethodArgumentsTransformer.java         |  36 +
 .../TypeCoercingMethodArgumentsTransformer.java    | 109 +++
 .../api/internal/coerce/TypeCoercionException.java |  28 +
 .../api/internal/file/AbstractFileResolver.java    |   8 +
 .../api/internal/file/AbstractFileResource.java    |   3 -
 .../internal/file/AntFileCollectionBuilder.groovy  |   5 +-
 .../api/internal/file/BaseDirFileResolver.java     |   3 -
 .../file/CopyActionProcessingStreamAction.java     |  25 +
 .../internal/file/DefaultCompositeFileTree.java    |  45 ++
 .../api/internal/file/DefaultFileOperations.java   |  42 +-
 .../file/DefaultTemporaryFileProvider.java         |   3 +-
 .../gradle/api/internal/file/FileOperations.java   |   6 +
 .../org/gradle/api/internal/file/FileResolver.java |   8 +
 .../org/gradle/api/internal/file/FileResource.java |   3 -
 .../api/internal/file/IdentityFileResolver.java    |   7 +-
 .../internal/file/MaybeCompressedFileResource.java |   3 -
 .../api/internal/file/archive/TarCopyAction.java   | 117 ++++
 .../internal/file/archive/TarCopySpecVisitor.java  |  88 ---
 .../api/internal/file/archive/ZipCopyAction.java   |  89 ++-
 .../internal/file/archive/ZipCopySpecVisitor.java  |  81 ---
 .../file/archive/compression/Bzip2Archiver.java    |   3 -
 .../file/archive/compression/GzipArchiver.java     |   3 -
 .../file/archive/compression/SimpleCompressor.java |   3 -
 .../collections/DefaultConfigurableFileTree.java   |  39 +-
 .../file/collections/DelegatingFileCollection.java | 111 +++
 .../file/collections/DirectoryFileTree.java        |   4 +-
 .../LazilyInitializedFileCollection.java           |  35 +
 .../collections/SingleIncludePatternFileTree.java  |   1 -
 .../internal/file/copy/AbstractZipCompressor.java  |   2 +
 .../api/internal/file/copy/ArchiveCopyAction.java  |  25 -
 .../gradle/api/internal/file/copy/CopyAction.java  |  49 +-
 .../api/internal/file/copy/CopyActionExecuter.java |  41 ++
 .../api/internal/file/copy/CopyActionImpl.java     | 235 +------
 .../file/copy/CopyActionProcessingStream.java      |  25 +
 .../CopySpecBackedCopyActionProcessingStream.java  |  68 ++
 .../api/internal/file/copy/CopySpecImpl.java       | 461 ------------
 .../api/internal/file/copy/CopySpecInternal.java   |  45 ++
 .../api/internal/file/copy/CopySpecSource.java     |   2 +-
 .../api/internal/file/copy/CopySpecVisitor.java    |  36 -
 .../api/internal/file/copy/DefaultCopySpec.java    | 462 ++++++++++++
 .../internal/file/copy/DefaultFileCopyDetails.java | 218 ++++++
 .../api/internal/file/copy/DelegatingCopySpec.java | 224 ++++++
 .../file/copy/DelegatingCopySpecVisitor.java       |  54 --
 .../api/internal/file/copy/DeleteActionImpl.java   |   3 -
 .../file/copy/DestinationRootCopySpec.java         |  51 ++
 .../copy/DuplicateHandlingCopyActionDecorator.java |  66 ++
 .../internal/file/copy/EmptyCopySpecVisitor.java   |  39 --
 .../gradle/api/internal/file/copy/FileCopier.java  |  66 ++
 .../api/internal/file/copy/FileCopyAction.java     |  32 +-
 .../api/internal/file/copy/FileCopyActionImpl.java |  33 +-
 .../file/copy/FileCopyDetailsInternal.java         |  25 +
 .../internal/file/copy/FileCopySpecVisitor.java    |  61 --
 .../internal/file/copy/MappingCopySpecVisitor.java | 230 ------
 .../api/internal/file/copy/MatchingCopyAction.java |  40 ++
 .../file/copy/NormalizingCopyActionDecorator.java  | 189 +++++
 .../file/copy/NormalizingCopySpecVisitor.java      | 121 ----
 .../api/internal/file/copy/ReadableCopySpec.java   |  32 +-
 .../api/internal/file/copy/RegExpNameMapper.java   |   3 -
 .../internal/file/copy/RelativizedCopySpec.java    |  51 ++
 .../file/copy/SyncCopyActionDecorator.java         |  92 +++
 .../internal/file/copy/SyncCopySpecVisitor.java    |  90 ---
 .../file/pattern/DefaultPatternMatcher.java        |   5 +-
 .../internal/file/pattern/GreedyPatternStep.java   |   3 -
 .../file/pattern/NameOnlyPatternMatcher.java       |   1 -
 .../file/pattern/PatternMatcherFactory.java        |   3 -
 .../api/internal/file/pattern/PatternStep.java     |   3 -
 .../internal/file/pattern/RegExpPatternStep.java   |   3 -
 .../internal/filestore/AbstractFileStoreEntry.java |  28 -
 .../gradle/api/internal/filestore/FileStore.java   |  31 -
 .../api/internal/filestore/FileStoreEntry.java     |  29 -
 .../api/internal/filestore/FileStoreSearcher.java  |  25 -
 .../filestore/GroupedAndNamedUniqueFileStore.java  |  11 +-
 .../api/internal/filestore/PathKeyFileStore.java   |  26 +-
 .../filestore/PathNormalisingKeyFileStore.java     |  11 +-
 .../internal/filestore/UniquePathKeyFileStore.java |   7 +-
 .../initialization/AbstractScriptHandler.java      |   4 +-
 .../initialization/DefaultScriptHandler.java       |   2 +-
 .../DefaultScriptHandlerFactory.java               |   2 +-
 .../NoClassLoaderUpdateScriptHandler.java          |   2 +-
 .../internal/notations/NotationParserBuilder.java  |   3 -
 .../gradle/api/internal/notations/TypeInfo.java    |   4 +-
 .../api/internal/notations/api/NotationParser.java |   3 -
 .../parsers/ClosureToSpecNotationParser.java       |   3 -
 .../notations/parsers/CompositeNotationParser.java |   3 -
 .../parsers/ErrorHandlingNotationParser.java       |   3 -
 .../notations/parsers/JustReturningParser.java     |   3 -
 .../notations/parsers/MapNotationParser.java       |   2 +-
 .../notations/parsers/NormalizedTimeUnit.java      |   3 -
 .../notations/parsers/TimeUnitsParser.java         |   3 -
 .../notations/parsers/TypedNotationParser.java     |   9 +-
 .../api/internal/plugins/DefaultConvention.java    |  11 +-
 .../plugins/DefaultObjectConfigurationAction.java  |  18 +-
 .../internal/plugins/DefaultPluginContainer.java   | 107 +++
 .../internal/plugins/DefaultPluginRegistry.java    |   4 -
 .../plugins/DefaultProjectsPluginContainer.java    | 110 ---
 .../plugins/ExtensionContainerInternal.java        |  29 +
 .../api/internal/plugins/ExtensionsStorage.java    |  28 +-
 .../api/internal/plugins/PluginRegistry.java       |   3 -
 .../api/internal/project/AbstractPluginAware.java  |  45 ++
 .../api/internal/project/AbstractProject.java      |  76 +-
 .../api/internal/project/DefaultAntBuilder.groovy  |   2 +-
 .../internal/project/DefaultAntBuilderFactory.java |   3 -
 .../project/DefaultIsolatedAntBuilder.groovy       |   4 +
 .../api/internal/project/DefaultProject.java       |   1 +
 .../project/DefaultProjectAccessListener.java      |   3 -
 .../internal/project/DefaultProjectRegistry.java   |   9 +-
 .../internal/project/GlobalServicesRegistry.java   | 113 ---
 .../project/GradleInternalServiceRegistry.java     |  92 ---
 .../api/internal/project/IProjectFactory.java      |   2 -
 .../api/internal/project/IProjectRegistry.java     |  40 --
 .../api/internal/project/ProjectFactory.java       |   5 +-
 .../api/internal/project/ProjectInternal.java      |   9 +-
 .../project/ProjectInternalServiceRegistry.java    | 180 -----
 .../api/internal/project/ProjectRegistry.java      |  37 +
 .../api/internal/project/ProjectStateInternal.java |   4 +
 .../internal/project/ServiceRegistryFactory.java   |  31 -
 .../internal/project/TaskExecutionServices.java    |  86 ---
 .../project/TaskInternalServiceRegistry.java       |  61 --
 .../project/TopLevelBuildServiceRegistry.java      | 253 -------
 .../internal/project/ant/AntLoggingAdapter.java    |   3 -
 .../AnnotationProcessingTaskFactory.java           | 200 ++++--
 .../internal/project/taskfactory/ITaskFactory.java |   3 -
 .../internal/project/taskfactory/TaskFactory.java  |  27 +-
 .../internal/resources/DefaultResourceHandler.java |   3 -
 .../gradle/api/internal/resources/URIBuilder.java  |   3 -
 .../gradle/api/internal/specs/ExplainingSpecs.java |   3 -
 .../tasks/CachingTaskDependencyResolveContext.java |   6 +-
 .../api/internal/tasks/ContextAwareTaskAction.java |  24 +
 .../api/internal/tasks/DefaultTaskContainer.java   | 109 ++-
 .../api/internal/tasks/SimpleWorkResult.java       |  30 +
 .../api/internal/tasks/TaskContainerInternal.java  |  22 +-
 .../gradle/api/internal/tasks/TaskExecuter.java    |   2 +-
 .../api/internal/tasks/TaskExecutionContext.java   |  24 +
 .../api/internal/tasks/TaskStatusNagger.java       |   9 +-
 .../execution/DefaultTaskExecutionContext.java     |  31 +
 .../execution/ExecuteActionsTaskExecuter.java      |  26 +-
 .../execution/ExecuteAtMostOnceTaskExecuter.java   |   5 +-
 .../PostExecutionAnalysisTaskExecuter.java         |   5 +-
 .../SkipEmptySourceFilesTaskExecuter.java          |   5 +-
 .../tasks/execution/SkipOnlyIfTaskExecuter.java    |   5 +-
 .../execution/SkipTaskWithNoActionsExecuter.java   |   5 +-
 .../tasks/execution/SkipUpToDateTaskExecuter.java  |  39 +-
 .../tasks/execution/ValidatingTaskExecuter.java    |   5 +-
 .../gradle/api/internal/xml/SimpleXmlWriter.java   |   2 -
 .../groovy/org/gradle/api/invocation/Gradle.java   |   3 +-
 .../groovy/org/gradle/api/logging/LogLevel.java    |   2 -
 .../groovy/org/gradle/api/logging/Logging.java     |   2 -
 .../groovy/org/gradle/api/plugins/Convention.java  |   2 -
 .../org/gradle/api/plugins/ExtensionContainer.java |   2 +
 .../groovy/org/gradle/api/plugins/PluginAware.java |  62 ++
 .../org/gradle/api/plugins/PluginCollection.java   |   8 +-
 .../org/gradle/api/plugins/PluginContainer.java    |   2 -
 .../api/plugins/PluginInstantiationException.java  |   2 -
 .../gradle/api/plugins/UnknownPluginException.java |   2 -
 .../main/groovy/org/gradle/api/specs/Specs.java    |   2 -
 .../org/gradle/api/tasks/AbstractCopyTask.java     |  83 ++-
 .../org/gradle/api/tasks/AntBuilderAware.groovy    |   2 -
 .../org/gradle/api/tasks/ConventionValue.java      |   1 -
 .../src/main/groovy/org/gradle/api/tasks/Copy.java |  79 ++-
 .../main/groovy/org/gradle/api/tasks/Delete.java   |   2 -
 .../src/main/groovy/org/gradle/api/tasks/Exec.java |   2 -
 .../groovy/org/gradle/api/tasks/GradleBuild.java   |  12 +-
 .../main/groovy/org/gradle/api/tasks/JavaExec.java |   5 +-
 .../org/gradle/api/tasks/StopActionException.java  |   2 -
 .../gradle/api/tasks/StopExecutionException.java   |   2 -
 .../src/main/groovy/org/gradle/api/tasks/Sync.java |  32 +-
 .../groovy/org/gradle/api/tasks/TaskContainer.java | 123 +++-
 .../main/groovy/org/gradle/api/tasks/Upload.java   |  19 +-
 .../api/tasks/bundling/AbstractArchiveTask.java    |   3 +-
 .../org/gradle/api/tasks/bundling/Compression.java |   2 -
 .../groovy/org/gradle/api/tasks/bundling/Tar.java  |  42 +-
 .../groovy/org/gradle/api/tasks/bundling/Zip.java  |  59 +-
 .../tasks/incremental/IncrementalTaskInputs.java   | 122 ++++
 .../api/tasks/incremental/InputFileDetails.java    |  50 ++
 .../gradle/api/tasks/incremental/package-info.java |  20 +
 .../org/gradle/api/tasks/util/PatternSet.java      |   7 +-
 .../groovy/org/gradle/cache/CacheValidator.java    |   2 +-
 .../gradle/cache/internal/DefaultCacheAccess.java  | 233 ++++---
 .../cache/internal/DefaultFileLockManager.java     | 242 +++++--
 .../org/gradle/cache/internal/FileAccess.java      |   2 +-
 .../groovy/org/gradle/cache/internal/FileLock.java |   5 +
 .../cache/internal/FileLockCommunicator.java       |  87 +++
 .../org/gradle/cache/internal/FileLockManager.java |   9 +
 .../cache/internal/GracefullyStoppedException.java |  19 +
 .../MultiProcessSafePersistentIndexedCache.java    |   2 +-
 .../gradle/cache/internal/OnDemandFileAccess.java  |   4 +
 .../gradle/cache/internal/SimpleStateCache.java    |  14 +-
 .../btree/BTreePersistentIndexedCache.java         |  16 +-
 .../cacheops/CacheAccessOperationsStack.java       |  90 +++
 .../cache/internal/cacheops/CacheOperation.java    |  27 +
 .../internal/cacheops/CacheOperationStack.java     |  70 ++
 .../DefaultFileLockContentionHandler.java          | 160 +++++
 .../locklistener/FileLockContentionHandler.java    |  27 +
 .../NoOpFileLockContentionHandler.java             |  31 +
 .../gradle/configuration/BuildScriptProcessor.java |  45 --
 .../configuration/DefaultBuildConfigurer.java      |  22 +-
 .../configuration/DefaultScriptPluginFactory.java  |  21 +-
 .../configuration/ImplicitTasksConfigurer.java     |  13 +-
 .../org/gradle/configuration/ImportsReader.java    |  34 +-
 .../configuration/LifecycleProjectEvaluator.java   |  51 --
 .../ProjectDependencies2TaskResolver.java          |  47 --
 .../org/gradle/configuration/ProjectEvaluator.java |  23 -
 .../project/BuildScriptProcessor.java              |  43 ++
 .../project/ConfigureActionsProjectEvaluator.java  |  37 +
 ...DefaultProjectConfigurationActionContainer.java |  45 ++
 .../project/DelayedConfigurationActions.java       |  33 +
 .../project/LifecycleProjectEvaluator.java         |  82 +++
 .../project/PluginsProjectConfigureActions.java    |  34 +
 .../ProjectConfigurationActionContainer.java       |  40 ++
 .../project/ProjectConfigureAction.java            |  30 +
 .../project/ProjectDependencies2TaskResolver.java  |  41 ++
 .../configuration/project/ProjectEvaluator.java    |  23 +
 .../gradle/execution/ProjectEvaluatingAction.java  |   2 -
 .../org/gradle/execution/TaskNameResolver.java     |  47 +-
 .../gradle/execution/TaskPathProjectEvaluator.java |   3 -
 .../org/gradle/execution/TaskSelectionResult.java  |  23 +
 .../groovy/org/gradle/execution/TaskSelector.java  |  23 +-
 .../commandline/CommandLineTaskConfigurer.java     |   6 +-
 .../commandline/CommandLineTaskParser.java         |   3 -
 .../taskgraph/AbstractTaskPlanExecutor.java        |  87 +++
 .../taskgraph/DefaultTaskExecutionPlan.java        | 297 ++++++--
 .../taskgraph/DefaultTaskGraphExecuter.java        |  42 +-
 .../taskgraph/DefaultTaskPlanExecutor.java         |  45 +-
 .../taskgraph/ParallelTaskPlanExecutor.java        |  82 +--
 .../execution/taskgraph/TaskDependencyGraph.java   |  52 ++
 .../execution/taskgraph/TaskExecutionPlan.java     |  17 +-
 .../org/gradle/execution/taskgraph/TaskInfo.java   | 112 ++-
 .../taskgraph/TaskPlanExecutorFactory.java         |   7 +-
 .../taskpath/ProjectFinderByTaskPath.java          |   3 -
 .../execution/taskpath/ResolvedTaskPath.java       |   3 -
 .../execution/taskpath/TaskPathResolver.java       |   3 -
 .../org/gradle/groovy/scripts/BasicScript.java     |   8 +-
 .../org/gradle/groovy/scripts/DefaultScript.java   |  18 +-
 .../scripts/DefaultScriptCompilerFactory.java      |   7 +-
 .../groovy/scripts/ScriptCompilerFactory.java      |   2 -
 .../internal/AsmBackedEmptyScriptGenerator.java    |  10 +-
 .../internal/ClasspathScriptTransformer.java       |   4 +-
 .../internal/DefaultScriptCompilationHandler.java  |   3 -
 .../scripts/internal/ScriptCompilationHandler.java |   3 -
 .../gradle/initialization/AbstractProjectSpec.java |   8 +-
 .../org/gradle/initialization/BaseSettings.java    |  63 +-
 .../org/gradle/initialization/BuildAction.java     |  31 +
 .../org/gradle/initialization/BuildController.java |  45 ++
 .../initialization/BuildFileProjectSpec.java       |   4 +-
 .../initialization/BuildLayoutParameters.java      |  25 +-
 .../gradle/initialization/BuildSourceBuilder.java  | 155 -----
 .../gradle/initialization/ClassLoaderRegistry.java |  16 +-
 .../initialization/DefaultClassLoaderRegistry.java |  14 +-
 .../DefaultCommandLineConverter.java               |  34 +-
 .../initialization/DefaultGradleLauncher.java      |   2 +-
 .../DefaultGradleLauncherFactory.java              |  28 +-
 .../DefaultGradlePropertiesLoader.java             |   3 -
 .../initialization/DefaultProjectDescriptor.java   |  16 +-
 .../DefaultProjectDescriptorRegistry.java          |   5 +-
 .../gradle/initialization/DefaultProjectSpec.java  |   4 +-
 .../gradle/initialization/DefaultSettings.groovy   |  13 +-
 .../initialization/DefaultSettingsFinder.java      |   3 -
 .../initialization/DependencyResolutionLogger.java |   1 -
 .../initialization/GradleLauncherAction.java       |  43 --
 .../initialization/GradleLauncherFactory.java      |   2 -
 .../initialization/IGradlePropertiesLoader.java    |   3 -
 .../initialization/IProjectDescriptorRegistry.java |  26 -
 .../org/gradle/initialization/ISettingsFinder.java |   3 -
 .../initialization/InstantiatingBuildLoader.java   |   3 -
 .../initialization/LayoutCommandLineConverter.java |   9 +-
 .../initialization/ProjectAccessListener.java      |   2 -
 .../initialization/ProjectDescriptorRegistry.java  |  23 +
 .../ProjectDirectoryProjectSpec.java               |   4 +-
 .../ProjectPropertySettingBuildLoader.java         |   3 -
 .../org/gradle/initialization/ProjectSpec.java     |   6 +-
 .../PropertiesLoadingSettingsProcessor.java        |   6 +-
 .../ScriptEvaluatingSettingsProcessor.java         |  12 +-
 .../org/gradle/initialization/SettingsFactory.java |  17 +-
 .../org/gradle/initialization/SettingsHandler.java |   9 +-
 .../gradle/initialization/SettingsProcessor.java   |   6 +-
 .../buildsrc/BuildSourceBuilder.java               |  86 +++
 .../buildsrc/BuildSrcBuildListenerFactory.java     |  61 ++
 .../buildsrc/BuildSrcUpdateFactory.java            |  61 ++
 .../featurelifecycle/DeprecatedFeatureHandler.java |  26 +
 .../featurelifecycle/DeprecatedFeatureUsage.java   |  77 ++
 .../LoggingDeprecatedFeatureHandler.java           |  76 ++
 .../ScriptUsageLocationReporter.java               |  91 +++
 .../featurelifecycle/UsageLocationReporter.java    |  21 +
 .../internal/graph/CachingDirectedGraphWalker.java | 203 ++++++
 .../org/gradle/internal/graph/DirectedGraph.java   |  26 +
 .../internal/graph/DirectedGraphRenderer.java      |  80 +++
 .../graph/DirectedGraphWithEdgeValues.java         |  25 +
 .../org/gradle/internal/graph/GraphAggregator.java |  90 +++
 .../gradle/internal/graph/GraphNodeRenderer.java   |  22 +
 .../org/gradle/internal/graph/GraphRenderer.java   |  69 ++
 .../service/scopes/BuildScopeServices.java         | 265 +++++++
 .../service/scopes/GlobalScopeServices.java        | 148 ++++
 .../service/scopes/GradleScopeServices.java        |  98 +++
 .../service/scopes/PluginServiceRegistry.java      |  28 +
 .../service/scopes/ProjectScopeServices.java       | 200 ++++++
 .../service/scopes/ServiceRegistryFactory.java     |  31 +
 .../service/scopes/SettingsScopeServices.java      |  59 ++
 .../service/scopes/TaskExecutionServices.java      |  93 +++
 .../internal/service/scopes/TaskScopeServices.java |  62 ++
 .../invocation/BuildClassLoaderRegistry.java       |  32 +
 .../DefaultBuildClassLoaderRegistry.java           |  39 ++
 .../org/gradle/invocation/DefaultGradle.java       |  54 +-
 .../org/gradle/logging/StandardOutputCapture.java  |   3 -
 .../logging/internal/DefaultLoggingManager.java    |   3 -
 .../logging/internal/EmbeddedLoggingServices.java  |   3 -
 .../internal/LoggingCommandLineConverter.java      |  59 +-
 .../gradle/logging/internal/LoggingConfigurer.java |   3 -
 .../gradle/logging/internal/NoOpLoggingSystem.java |   3 -
 .../logging/internal/StdErrLoggingSystem.java      |   3 -
 .../logging/internal/StdOutLoggingSystem.java      |   3 -
 .../internal/logback/LogbackLoggingConfigurer.java |   6 +-
 .../main/groovy/org/gradle/model/ModelType.java    |  28 +
 .../internal/PersistentModelObjectRegistry.java    | 136 ++++
 .../groovy/org/gradle/process/BaseExecSpec.java    |   2 -
 .../main/groovy/org/gradle/process/ExecResult.java |   2 -
 .../main/groovy/org/gradle/process/ExecSpec.java   |   2 -
 .../groovy/org/gradle/process/JavaExecSpec.java    |   2 -
 .../internal/AbstractExecHandleBuilder.java        |   3 -
 .../process/internal/BadExitCodeException.java     |   3 -
 .../gradle/process/internal/DefaultExecAction.java |   3 -
 .../gradle/process/internal/DefaultExecHandle.java |  11 +-
 .../process/internal/DefaultJavaExecAction.java    |   3 -
 .../internal/DefaultWorkerProcessFactory.java      |   2 +-
 .../org/gradle/process/internal/ExecAction.java    |   5 +-
 .../org/gradle/process/internal/ExecException.java |   3 -
 .../org/gradle/process/internal/ExecHandle.java    |   3 -
 .../gradle/process/internal/ExecHandleBuilder.java |   3 -
 .../process/internal/ExecHandleListener.java       |   3 -
 .../gradle/process/internal/ExecHandleRunner.java  |   3 -
 .../internal/ExecHandleShutdownHookAction.java     |   2 -
 .../gradle/process/internal/ExecHandleState.java   |   3 -
 .../gradle/process/internal/JavaExecAction.java    |   5 +-
 .../process/internal/ProcessBuilderFactory.java    |   2 -
 .../internal/ProcessParentingInitializer.java      |   1 -
 .../gradle/process/internal/ProcessSettings.java   |   3 -
 .../process/internal/child/EncodedStream.java      |   2 -
 .../child/ImplementationClassLoaderWorker.java     |   6 +-
 .../IsolatedApplicationClassLoaderWorker.java      |   2 +-
 .../child/SystemApplicationClassLoaderWorker.java  |   1 -
 .../child/WorkerProcessClassPathProvider.java      |   1 +
 .../shutdown/ShutdownHookActionRegister.java       |   3 -
 .../internal/streams/ExecOutputHandleRunner.java   |   3 -
 .../process/internal/streams/SafeStreams.java      |   3 -
 .../process/internal/streams/StreamsForwarder.java |   8 +-
 .../process/internal/streams/StreamsHandler.java   |   3 -
 .../groovy/org/gradle/profile/BuildProfile.java    | 126 ++--
 .../org/gradle/profile/CompositeOperation.java     |   4 +
 .../org/gradle/profile/ContinuousOperation.java    |  20 +-
 .../gradle/profile/DependencyResolveProfile.java   |  30 -
 .../org/gradle/profile/EvalutationOperation.java   |  31 -
 .../main/groovy/org/gradle/profile/Operation.java  |  21 +
 .../org/gradle/profile/ProfileEventAdapter.java    |  40 +-
 .../org/gradle/profile/ProfileReportRenderer.java  |  59 +-
 .../groovy/org/gradle/profile/ProjectProfile.java  |  54 +-
 .../groovy/org/gradle/profile/TaskExecution.java   |  23 +-
 .../org/gradle/reporting/ReportRenderer.java       |   4 +-
 .../testfixtures/internal/GlobalTestServices.java  |  60 --
 .../internal/InMemoryCacheFactory.java             |   1 -
 .../internal/InMemoryIndexedCache.java             |  70 ++
 .../testfixtures/internal/ProjectBuilderImpl.java  |  17 +-
 .../internal/TestBuildScopeServices.java           |  52 ++
 .../internal/TestGlobalScopeServices.java          |  60 ++
 .../internal/TestTopLevelBuildServiceRegistry.java |  52 --
 .../provider/model/ToolingModelBuilder.java        |  29 +
 .../model/ToolingModelBuilderRegistry.java         |  29 +
 .../provider/model/UnknownModelException.java      |  31 +
 .../DefaultToolingModelBuilderRegistry.java        |  64 ++
 .../model/internal/LegacyConsumerInterface.java    |  25 +
 .../tooling/provider/model/package-info.java       |  20 +
 .../src/main/groovy/org/gradle/util/AntUtil.java   |   7 +-
 .../org/gradle/util/AvailablePortFinder.java       |   2 -
 .../util/ClassLoaderBackedClasspathSource.java     |  43 --
 .../groovy/org/gradle/util/ClassLoaderFactory.java |  41 --
 .../groovy/org/gradle/util/ClasspathSource.java    |  23 -
 .../main/groovy/org/gradle/util/ClasspathUtil.java |  98 ---
 .../src/main/groovy/org/gradle/util/Clock.java     |   3 -
 .../main/groovy/org/gradle/util/ConfigureUtil.java |   3 -
 .../org/gradle/util/DefaultClassLoaderFactory.java |  86 ---
 .../main/groovy/org/gradle/util/DeleteOnExit.java  |  52 --
 .../org/gradle/util/FilteringClassLoader.java      | 212 ------
 .../main/groovy/org/gradle/util/GFileUtils.java    |  17 -
 .../main/groovy/org/gradle/util/GStreamUtil.java   |  33 +
 .../src/main/groovy/org/gradle/util/GUtil.java     |   3 -
 .../main/groovy/org/gradle/util/GradleVersion.java | 116 ++--
 .../src/main/groovy/org/gradle/util/JarUtil.java   |   5 +-
 .../main/groovy/org/gradle/util/JavaMethod.java    |  75 --
 .../org/gradle/util/LineBufferingOutputStream.java |   2 -
 .../org/gradle/util/MultiParentClassLoader.java    | 103 ---
 .../org/gradle/util/MutableURLClassLoader.java     |  48 --
 .../core/src/main/groovy/org/gradle/util/Path.java |   3 -
 .../groovy/org/gradle/util/ReflectionUtil.groovy   |  46 --
 .../org/gradle/util/SingleMessageLogger.java       | 185 ++---
 .../src/main/groovy/org/gradle/util/TextUtil.java  |   7 +
 .../main/groovy/org/gradle/util/VersionNumber.java |  19 +-
 .../gradle/util/internal/LimitedDescription.java   |   2 -
 .../org/gradle/configuration/default-imports.txt   |  38 -
 .../buildsrc/defaultBuildSourceScript.txt          |   6 +
 .../initialization/defaultBuildSourceScript.txt    |   5 -
 .../groovy/org/gradle/StartParameterTest.groovy    |  28 -
 .../org/gradle/api/AllGradleExceptionsTest.groovy  |   6 +-
 .../org/gradle/api/file/ProjectCopySpecTest.groovy | 104 +++
 .../AbstractNamedDomainObjectContainerTest.groovy  | 166 +++--
 .../gradle/api/internal/AbstractTaskSpec.groovy    |   4 +-
 .../AsmBackedClassGeneratorGroovyTest.groovy       | 221 +++++-
 .../api/internal/AsmBackedClassGeneratorTest.java  |   4 +-
 .../internal/CachingDirectedGraphWalkerTest.groovy | 197 ------
 .../api/internal/ChainingTransformerTest.java      |  12 +-
 .../internal/CompositeDomainObjectSetTest.groovy   |  29 +-
 .../api/internal/ConventionAwareHelperTest.java    |  15 +-
 .../DefaultDomainObjectCollectionTest.java         |  24 +-
 .../internal/DefaultNamedDomainObjectSetTest.java  |  20 +-
 ...PolymorphicDomainObjectContainerBaseTest.groovy |  43 ++
 ...tPolymorphicDomainObjectContainerDslTest.groovy |  88 ++-
 ...aultPolymorphicDomainObjectContainerTest.groovy |  63 +-
 .../org/gradle/api/internal/DefaultTaskTest.groovy |  37 +-
 .../api/internal/DocumentationRegistryTest.groovy  |  47 +-
 .../api/internal/ExtensibleDynamicObjectTest.java  |   6 +-
 .../gradle/api/internal/GraphAggregatorTest.groovy |  65 --
 .../artifacts/DefaultExcludeRuleContainerTest.java |   3 -
 .../artifacts/ProjectBackedModuleTest.groovy       |   4 +-
 .../AbstractModuleDependencySpec.groovy            |   4 +-
 .../dependencies/AbstractModuleDependencyTest.java |   7 +-
 .../dependencies/DefaultClientModuleTest.java      |   3 -
 .../DefaultDependencyArtifactTest.java             |   6 +-
 .../DefaultExternalModuleDependencyTest.java       |   3 -
 .../DefaultProjectDependencyTest.groovy            |  14 +-
 .../DefaultDependencyHandlerTest.groovy            |   8 +-
 .../dependencies/ModuleFactoryDelegateTest.java    |   9 +-
 .../publish/AbstractPublishArtifactTest.java       |   6 +-
 .../publish/ArchivePublishArtifactTest.groovy      |   3 -
 .../publish/DefaultPublishArtifactTest.java        |   3 -
 ...meAfterContainerInclusionDeprecationTest.groovy |  11 +-
 .../LatestVersionSemanticComparatorSpec.groovy     |  76 --
 .../CacheBackedFileSnapshotRepositoryTest.groovy   |  65 --
 .../changedetection/CachingHasherTest.java         | 108 ---
 .../CompositeUpToDateRuleTest.groovy               |  69 --
 .../DefaultFileSnapshotterTest.groovy              | 330 ---------
 .../DefaultTaskArtifactStateCacheAccessTest.groovy |  53 --
 .../DefaultTaskArtifactStateRepositoryTest.java    | 606 ----------------
 ...BroadcastTaskArtifactStateRepositoryTest.groovy | 107 ---
 ...hortCircuitTaskArtifactStateRepositoryTest.java | 165 -----
 .../DefaultTaskArtifactStateRepositoryTest.groovy  | 683 ++++++++++++++++++
 ...rtCircuitTaskArtifactStateRepositoryTest.groovy | 112 +++
 .../rules/CachingTaskStateChangesTest.groovy       |  84 +++
 .../rules/InputFilesStateChangeRuleTest.groovy     |  78 +++
 .../rules/OutputFilesStateChangeRuleTest.groovy    |  82 +++
 .../rules/SimpleTaskStateChangesTest.groovy        |  56 ++
 .../rules/SummaryTaskStateChangesTest.groovy       |  89 +++
 .../CacheBackedFileSnapshotRepositoryTest.groovy   |  66 ++
 .../changedetection/state/CachingHasherTest.java   | 108 +++
 .../DefaultFileSnapshotterSerializerTest.groovy    |  38 +
 .../state/DefaultFileSnapshotterTest.groovy        | 346 +++++++++
 .../DefaultTaskArtifactStateCacheAccessTest.groovy |  55 ++
 .../state/FileSnapshotSerializerTest.groovy        |  45 ++
 .../state/OutputFilesSnapshotSerializerTest.groovy |  36 +
 ...peCoercingMethodArgumentsTransformerTest.groovy |  67 ++
 .../internal/file/AbstractFileCollectionTest.java  |  10 +-
 .../internal/file/BaseDirFileResolverTest.groovy   |   3 -
 .../internal/file/CompositeFileCollectionTest.java |   6 +-
 .../api/internal/file/CompositeFileTreeTest.java   |   6 +-
 .../file/DefaultCompositeFileTreeTest.groovy       |  68 ++
 .../internal/file/DefaultFileOperationsTest.groovy |  25 +-
 .../file/DelegatingFileCollectionTest.groovy       | 104 +++
 .../LazilyInitializedFileCollectionTest.groovy     |  52 ++
 .../file/MaybeCompressedFileResourceTest.groovy    |   3 -
 .../internal/file/archive/TarCopyActionTest.java   | 244 +++++++
 .../file/archive/TarCopySpecVisitorTest.java       | 250 -------
 .../internal/file/archive/ZipCopyActionTest.java   | 215 ++++++
 .../file/archive/ZipCopySpecVisitorTest.java       | 245 -------
 .../file/archive/compression/ArchiversTest.groovy  |   3 -
 .../DefaultConfigurableFileCollectionTest.java     |  12 +-
 .../DefaultConfigurableFileTreeTest.groovy         |  17 +-
 .../file/collections/DirectoryFileTreeTest.java    |   6 +-
 .../internal/file/collections/MapFileTreeTest.java |   8 +-
 .../file/copy/CopyActionExecuterTest.groovy        |  74 ++
 .../internal/file/copy/CopyActionImplTest.groovy   |  95 ---
 .../api/internal/file/copy/CopySpecImplTest.groovy | 350 ----------
 .../internal/file/copy/CopySpecMatchingTest.groovy |  88 +++
 .../internal/file/copy/DefaultCopySpecTest.groovy  | 394 +++++++++++
 .../internal/file/copy/DeleteActionImplTest.groovy |   3 -
 ...DuplicateHandlingCopyActionDecoratorTest.groovy | 196 ++++++
 .../internal/file/copy/FileCopyActionImplTest.java |  54 --
 .../api/internal/file/copy/FileCopyActionTest.java |  67 ++
 .../file/copy/FileCopySpecVisitorTest.java         |  92 ---
 .../api/internal/file/copy/FilterChainTest.java    |   4 +-
 .../file/copy/MappingCopySpecVisitorTest.java      | 400 -----------
 .../copy/NormalizingCopyActionDecoratorTest.java   | 151 ++++
 .../file/copy/NormalizingCopySpecVisitorTest.java  | 226 ------
 .../file/copy/SyncCopyActionDecoratorTest.groovy   |  58 ++
 .../file/copy/SyncCopySpecVisitorTest.java         | 169 -----
 .../file/pattern/RegExpPatternStepTest.java        |   4 +-
 .../DefaultScriptHandlerFactoryTest.groovy         |   2 +-
 .../initialization/DefaultScriptHandlerTest.groovy |  97 ++-
 .../parsers/ClosureToSpecNotationParserTest.groovy |   3 -
 .../notations/parsers/TimeUnitsParserTest.groovy   |   3 -
 .../parsers/TypedNotationParserTest.groovy         |   3 -
 .../internal/plugins/DefaultConventionTest.groovy  |   6 +-
 .../plugins/DefaultPluginContainerTest.java        | 128 ++++
 .../DefaultProjectsPluginContainerTest.java        | 131 ----
 .../internal/plugins/ExtensionContainerTest.groovy |   3 -
 .../internal/plugins/ExtensionsStorageTest.groovy  |  76 ++
 .../project/DefaultAntBuilderFactoryTest.groovy    |   4 +-
 .../internal/project/DefaultAntBuilderTest.groovy  |   4 +-
 .../project/DefaultIsolatedAntBuilderTest.groovy   |   4 +-
 .../project/DefaultProjectRegistryTest.java        |  23 +-
 .../api/internal/project/DefaultProjectTest.groovy |  81 ++-
 .../project/GlobalServicesRegistryTest.java        | 142 ----
 .../GradleInternalServiceRegistryTest.groovy       | 102 ---
 .../internal/project/NewDefaultProjectTest.groovy  |   7 +-
 .../api/internal/project/ProjectFactoryTest.java   |  16 +-
 .../ProjectInternalServiceRegistryTest.java        | 276 --------
 .../project/TaskExecutionServicesTest.groovy       |  54 --
 .../project/TaskInternalServiceRegistryTest.java   |  90 ---
 .../gradle/api/internal/project/TestPlugin1.groovy |   3 -
 .../gradle/api/internal/project/TestPlugin2.groovy |   3 -
 .../TopLevelBuildServiceRegistryTest.groovy        | 264 -------
 .../AnnotationProcessingTaskFactoryTest.java       |  95 ++-
 .../project/taskfactory/TaskFactoryTest.groovy     |  25 +-
 .../api/internal/resource/UriResourceTest.groovy   |   5 +-
 .../api/internal/resources/URIBuilderTest.groovy   |   3 -
 .../internal/tasks/DefaultTaskContainerTest.groovy |  84 ++-
 .../execution/ExecuteActionsTaskExecuterTest.java  |  79 ++-
 .../ExecuteAtMostOnceTaskExecuterTest.groovy       |  22 +-
 .../PostExecutionAnalysisTaskExecuterTest.groovy   |  66 +-
 .../SkipEmptySourceFilesTaskExecuterTest.groovy    |  12 +-
 .../execution/SkipOnlyIfTaskExecuterTest.java      |  11 +-
 .../SkipTaskWithNoActionsExecuterTest.groovy       |  14 +-
 .../execution/SkipUpToDateTaskExecuterTest.groovy  | 110 +++
 .../execution/SkipUpToDateTaskExecuterTest.java    | 141 ----
 .../execution/ValidatingTaskExecuterTest.groovy    |  16 +-
 .../tasks/util/DefaultJavaForkOptionsTest.groovy   |   9 +-
 .../api/internal/xml/SimpleXmlWriterSpec.groovy    |   5 +-
 .../api/plugins/TestPluginConvention1.groovy       |   3 -
 .../api/plugins/TestPluginConvention2.groovy       |   3 -
 .../gradle/api/tasks/AbstractCopyTaskTest.groovy   |  80 +++
 .../org/gradle/api/tasks/AbstractCopyTaskTest.java |  99 ---
 .../groovy/org/gradle/api/tasks/CopyTest.groovy    |  67 +-
 .../groovy/org/gradle/api/tasks/DeleteTest.java    |   3 -
 .../org/gradle/api/tasks/DirectoryTest.groovy      |   8 +-
 .../org/gradle/api/tasks/GradleBuildTest.groovy    |  63 +-
 .../groovy/org/gradle/api/tasks/SyncTest.groovy    |  27 +
 .../groovy/org/gradle/api/tasks/UploadTest.groovy  |   4 +-
 .../org/gradle/api/tasks/ant/AntTargetTest.java    |  10 +-
 .../org/gradle/api/tasks/bundling/TarTest.groovy   |  10 +-
 .../org/gradle/api/tasks/bundling/ZipTest.groovy   |   6 +-
 .../tasks/util/AbstractTestForPatternSet.groovy    |  12 +-
 .../cache/internal/DefaultCacheAccessTest.groovy   | 449 +++++-------
 .../cache/internal/DefaultCacheFactoryTest.groovy  |   3 +-
 .../internal/DefaultFileLockManagerTest.groovy     |  16 +-
 .../DefaultPersistentDirectoryCacheTest.java       |   3 +-
 .../cache/internal/FileLockCommunicatorTest.groovy |  99 +++
 .../cacheops/CacheAccessOperationsStackTest.groovy | 142 ++++
 .../cacheops/CacheOperationStackTest.groovy        | 126 ++++
 .../DefaultFileLockContentionHandlerTest.groovy    | 185 +++++
 .../configuration/BuildScriptProcessorTest.java    |  59 --
 .../DefaultBuildConfigurerTest.groovy              |  21 +-
 .../DefaultScriptPluginFactoryTest.java            |  13 +-
 .../ImplicitTasksConfigurerTest.groovy             |  33 -
 .../gradle/configuration/ImportsReaderTest.groovy  |  33 +-
 .../LifecycleProjectEvaluatorTest.groovy           |  90 ---
 .../ProjectDependencies2TaskResolverTest.groovy    |  40 --
 .../project/BuildScriptProcessorTest.groovy        |  43 ++
 .../ConfigureActionsProjectEvaluatorTest.groovy    |  59 ++
 ...tProjectConfigurationActionContainerTest.groovy |  52 ++
 .../project/DelayedConfigurationActionsTest.groovy |  70 ++
 .../project/LifecycleProjectEvaluatorTest.groovy   | 152 ++++
 .../PluginsProjectConfigureActionsTest.groovy      |  52 ++
 .../ProjectDependencies2TaskResolverTest.groovy    |  37 +
 .../execution/ProjectEvaluatingActionTest.groovy   |   3 -
 .../gradle/execution/TaskNameResolverTest.groovy   |  50 +-
 ...kNameResolvingBuildConfigurationActionTest.java |  43 +-
 .../execution/TaskPathProjectEvaluatorTest.groovy  |   3 -
 .../CommandLineTaskConfigurerSpec.groovy           |   3 -
 .../commandline/CommandLineTaskParserSpec.groovy   |  26 +-
 .../taskgraph/DefaultTaskExecutionPlanTest.groovy  | 613 +++++++++++-----
 .../taskgraph/DefaultTaskGraphExecuterTest.java    |  36 +-
 .../taskgraph/ParallelTaskExecutionPlanTest.groovy |  26 -
 .../taskgraph/TaskDependencyGraphTest.groovy       |  77 ++
 .../taskgraph/TaskPlanExecutorFactoryTest.groovy   |   2 +-
 .../taskpath/ProjectFinderByTaskPathTest.groovy    |  11 +-
 .../execution/taskpath/ResolvedTaskPathTest.groovy |   3 -
 .../execution/taskpath/TaskPathResolverTest.groovy |   3 -
 .../gradle/groovy/scripts/DefaultScriptTest.groovy |  15 +-
 .../org/gradle/groovy/scripts/EmptyScript.java     |   3 -
 .../DefaultScriptCompilationHandlerTest.java       |   3 -
 .../initialization/BuildFileProjectSpecTest.java   |   6 +-
 .../BuildLayoutParametersTest.groovy               |  51 ++
 .../initialization/BuildSourceBuilderTest.groovy   | 171 -----
 .../DefaultCommandLineConverterTest.java           |   3 -
 .../DefaultGradleLauncherFactoryTest.groovy        |   5 +-
 .../initialization/DefaultGradleLauncherTest.java  |  14 +-
 .../DefaultGradlePropertiesLoaderTest.java         |   3 -
 .../DefaultProjectDescriptorRegistryTest.java      |  19 +-
 .../DefaultProjectDescriptorTest.java              |  43 +-
 .../initialization/DefaultSettingsTest.groovy      |  94 ++-
 .../InstantiatingBuildLoaderTest.groovy            |  15 +-
 .../LayoutCommandLineConverterTest.groovy          |  45 +-
 .../ProjectDirectoryProjectSpecTest.java           |   6 +-
 .../ProjectPropertySettingBuildLoaderTest.groovy   |   7 +-
 .../ScriptEvaluatingSettingsProcessorTest.groovy   |  24 +-
 .../gradle/initialization/SettingsFactoryTest.java |  43 +-
 .../gradle/initialization/SettingsHandlerTest.java |  23 +-
 .../buildsrc/BuildSourceBuilderTest.groovy         |  58 ++
 .../BuildSrcBuildListenerFactoryTest.groovy        |  62 ++
 .../buildsrc/BuildSrcUpdateFactoryTest.groovy      |  67 ++
 .../DeprecatedFeatureUsageTest.groovy              |  38 +
 .../LoggingDeprecatedFeatureHandlerTest.groovy     |  57 ++
 .../ScriptUsageLocationReporterTest.groovy         | 126 ++++
 .../graph/CachingDirectedGraphWalkerTest.groovy    | 266 +++++++
 .../graph/DirectedGraphRendererTest.groovy         | 140 ++++
 .../internal/graph/GraphAggregatorTest.groovy      |  65 ++
 .../service/scopes/BuildScopeServicesTest.groovy   | 270 ++++++++
 .../service/scopes/GlobalScopeServicesTest.java    | 161 +++++
 .../service/scopes/GradleScopeServicesTest.groovy  | 119 ++++
 .../service/scopes/ProjectScopeServicesTest.groovy | 222 ++++++
 .../scopes/SettingsScopeServicesTest.groovy        |  75 ++
 .../scopes/TaskExecutionServicesTest.groovy        |  57 ++
 .../service/scopes/TaskScopeServicesTest.java      |  91 +++
 .../DefaultBuildClassLoaderRegistryTest.groovy     |  49 ++
 .../org/gradle/invocation/DefaultGradleTest.java   |  29 +-
 .../internal/DefaultLoggingManagerTest.java        |   6 +-
 .../LoggingCommandLineConverterTest.groovy         |  16 +-
 .../PersistentModelObjectRegistryTest.groovy       | 166 +++++
 .../org/gradle/model/internal/TestModel.java       |  52 ++
 .../process/internal/DefaultExecHandleSpec.groovy  |   3 -
 .../internal/DefaultWorkerProcessTest.groovy       |   2 +-
 .../internal/JavaExecHandleBuilderTest.groovy      |  15 +-
 .../gradle/process/internal/JvmOptionsTest.groovy  |  10 +-
 .../child/ImplementationClassLoaderWorkerTest.java |   2 +-
 .../internal/child/SerializableMockHelper.groovy   |   8 +-
 .../org/gradle/profile/BuildProfileTest.groovy     |  80 ++-
 .../profile/ProfileReportRendererTest.groovy       | 227 ++++++
 .../org/gradle/profile/ProjectProfileTest.groovy   |  34 +
 .../org/gradle/profile/TaskExecutionTest.groovy    |  43 ++
 .../DefaultToolingModelBuilderRegistryTest.groovy  |  76 ++
 .../util/DefaultClassLoaderFactoryTest.groovy      |  91 ---
 .../util/DefaultClassLoaderFactoryTestHelper.java  |  35 -
 .../gradle/util/FilteringClassLoaderTest.groovy    | 182 -----
 .../groovy/org/gradle/util/GFileUtilsTest.groovy   |   3 -
 .../groovy/org/gradle/util/GStreamUtilTest.groovy  |  38 +
 .../org/gradle/util/GradleVersionTest.groovy       | 124 ++--
 .../groovy/org/gradle/util/JavaMethodTest.java     |  68 --
 .../groovy/org/gradle/util/MatchersTest.groovy     |   3 -
 .../gradle/util/MultiParentClassLoaderTest.groovy  | 152 ----
 .../test/groovy/org/gradle/util/PathTest.groovy    |   6 +-
 .../org/gradle/util/SingleMessageLoggerTest.groovy |  49 +-
 .../test/groovy/org/gradle/util/StageTest.groovy   |   3 -
 .../groovy/org/gradle/util/TextUtilTest.groovy     |  26 +-
 .../org/gradle/util/VersionNumberTest.groovy       |  18 +-
 .../util/internal/ArgumentsSplitterTest.groovy     |   4 +-
 .../util/internal/LimitedDescriptionTest.groovy    |   3 -
 .../org/gradle/api/internal/file/TestFiles.java    |   7 +-
 .../internal/file/copy/CopyActionExecuterUtil.java |  40 ++
 .../api/tasks/AbstractConventionTaskTest.java      |  12 +-
 .../api/tasks/AbstractCopyTaskContractTest.groovy  |  44 ++
 .../gradle/api/tasks/AbstractSpockTaskTest.groovy  |  18 +-
 .../org/gradle/api/tasks/AbstractTaskTest.java     |  20 +-
 .../tasks/bundling/AbstractArchiveTaskTest.groovy  |  10 +-
 .../DefaultFileLockManagerTestHelper.groovy        |   8 +-
 .../org/gradle/util/ConcurrentSpecification.groovy |   3 -
 .../groovy/org/gradle/util/HelperUtil.groovy       | 143 ----
 .../groovy/org/gradle/util/TestTask.groovy         |   3 -
 .../groovy/org/gradle/util/TestUtil.groovy         | 140 ++++
 subprojects/cpp/cpp.gradle                         |   4 +-
 ...tLanguageIncrementalBuildIntegrationTest.groovy | 327 +++++++++
 .../cpp/AbstractLanguageIntegrationTest.groovy     | 208 ++++++
 .../cpp/AssemblyLanguageIntegrationTest.groovy     |  78 +++
 .../cpp/BinaryFlavorsIntegrationTest.groovy        | 217 ++++++
 .../cpp/CCallingCppLanguageIntegrationTest.groovy  |  25 +
 ...CLanguageIncrementalBuildIntegrationTest.groovy |  27 +
 .../language/cpp/CLanguageIntegrationTest.groovy   |  52 ++
 .../language/cpp/CppBinariesIntegrationTest.groovy | 219 ++++++
 .../cpp/CppCallingCLanguageIntegrationTest.groovy  |  25 +
 .../cpp/CppExePluginGoodBehaviourTest.groovy       |  25 +
 .../cpp/CppExePluginIntegrationTest.groovy         |  65 ++
 ...pLanguageIncrementalBuildIntegrationTest.groovy |  25 +
 .../language/cpp/CppLanguageIntegrationTest.groovy |  53 ++
 .../cpp/CppLibPluginGoodBehaviourTest.groovy       |  25 +
 .../cpp/CppLibPluginIntegrationTest.groovy         |  61 ++
 .../language/cpp/CppPluginGoodBehaviourTest.groovy |  25 +
 .../language/cpp/CppPluginIntegrationTest.groovy   | 138 ++++
 .../language/cpp/CppSamplesIntegrationTest.groovy  | 189 +++++
 .../cpp/MixedLanguageIntegrationTest.groovy        |  81 +++
 .../cpp/MultipleToolChainIntegrationTest.groovy    | 116 ++++
 .../cpp/NativeBinariesPluginIntegrationTest.groovy | 222 ++++++
 ...bstractInstalledToolChainIntegrationSpec.groovy |  51 ++
 .../language/cpp/fixtures/AvailableToolChains.java | 312 +++++++++
 .../language/cpp/fixtures/ExecutableFixture.groovy |  31 +
 .../cpp/fixtures/NativeBinaryFixture.groovy        |  67 ++
 .../cpp/fixtures/NativeInstallationFixture.groovy  |  73 ++
 .../cpp/fixtures/SharedLibraryFixture.groovy       |  43 ++
 .../cpp/fixtures/SingleToolChainTestRunner.java    |  76 ++
 .../fixtures/app/CCallingCppHelloWorldApp.groovy   |  75 ++
 .../cpp/fixtures/app/CHelloWorldApp.groovy         | 112 +++
 .../fixtures/app/CppCallingCHelloWorldApp.groovy   |  70 ++
 .../cpp/fixtures/app/CppHelloWorldApp.groovy       | 111 +++
 .../language/cpp/fixtures/app/HelloWorldApp.java   |  73 ++
 .../cpp/fixtures/app/IncrementalHelloWorldApp.java |  27 +
 .../fixtures/app/MixedLanguageHelloWorldApp.groovy | 162 +++++
 .../language/cpp/fixtures/app/SourceFile.java      |  52 ++
 .../cpp/AbstractBinariesIntegrationSpec.groovy     |  33 -
 .../org/gradle/plugins/cpp/AvailableCompilers.java | 171 -----
 .../cpp/CppExePluginGoodBehaviourTest.groovy       |  25 -
 .../plugins/cpp/CppIntegrationTestRunner.java      |  96 ---
 .../cpp/CppLibPluginGoodBehaviourTest.groovy       |  25 -
 .../plugins/cpp/CppPluginIntegrationTest.groovy    | 220 ------
 .../plugins/cpp/CppSamplesIntegrationTest.groovy   |  85 ---
 .../gradle/nativecode/base/DependentSourceSet.java |  45 ++
 .../org/gradle/nativecode/base/Executable.java     |  26 +
 .../gradle/nativecode/base/ExecutableBinary.java   |  26 +
 .../nativecode/base/ExecutableContainer.java       |  25 +
 .../groovy/org/gradle/nativecode/base/Flavor.java  |  34 +
 .../gradle/nativecode/base/FlavorContainer.java    |  27 +
 .../nativecode/base/HeaderExportingSourceSet.java  |  38 +
 .../groovy/org/gradle/nativecode/base/Library.java |  40 ++
 .../org/gradle/nativecode/base/LibraryBinary.java  |  30 +
 .../gradle/nativecode/base/LibraryContainer.java   |  25 +
 .../gradle/nativecode/base/LibraryResolver.java    |  32 +
 .../org/gradle/nativecode/base/NativeBinary.java   | 131 ++++
 .../gradle/nativecode/base/NativeComponent.java    |  73 ++
 .../nativecode/base/NativeDependencySet.java       |  40 ++
 .../nativecode/base/SharedLibraryBinary.java       |  25 +
 .../nativecode/base/StaticLibraryBinary.java       |  37 +
 .../org/gradle/nativecode/base/ToolChain.java      |  28 +
 .../gradle/nativecode/base/ToolChainRegistry.java  |  44 ++
 .../base/internal/AbstractToolChain.java           | 139 ++++
 .../nativecode/base/internal/BinaryToolSpec.java   |  27 +
 .../base/internal/ConfigurableLibraryResolver.java |  27 +
 .../ConfigurationBasedNativeDependencySet.groovy   |  90 +++
 .../base/internal/CreateNativeBinariesAction.java  |  57 ++
 .../base/internal/DefaultExecutable.java           |  30 +
 .../base/internal/DefaultExecutableBinary.java     |  39 ++
 .../base/internal/DefaultExecutableContainer.java  |  33 +
 .../nativecode/base/internal/DefaultFlavor.java    |  65 ++
 .../base/internal/DefaultFlavorContainer.java      |  57 ++
 .../nativecode/base/internal/DefaultLibrary.java   |  78 +++
 .../base/internal/DefaultLibraryContainer.java     |  37 +
 .../base/internal/DefaultLibraryResolver.java      |  56 ++
 .../base/internal/DefaultNativeBinary.java         | 150 ++++
 .../base/internal/DefaultNativeComponent.java      |  77 ++
 .../base/internal/DefaultSharedLibraryBinary.java  |  86 +++
 .../base/internal/DefaultStaticLibraryBinary.java  |  96 +++
 .../base/internal/DefaultToolChainRegistry.java    | 164 +++++
 .../base/internal/ExecutableLinkerSpec.java        |  20 +
 .../nativecode/base/internal/LinkerSpec.java       |  42 ++
 .../base/internal/NativeBinaryFactory.java         |  63 ++
 .../base/internal/NativeBinaryInternal.java        |  27 +
 .../internal/ResolvableNativeDependencySet.java    |  61 ++
 .../base/internal/SharedLibraryLinkerSpec.java     |  23 +
 .../base/internal/SourceSetNotationParser.java     |  73 ++
 .../base/internal/StaticLibraryArchiverSpec.java   |  34 +
 .../base/internal/ToolChainAvailability.java       |  48 ++
 .../base/internal/ToolChainInternal.java           |  47 ++
 .../org/gradle/nativecode/base/package-info.java   |  20 +
 .../base/plugins/NativeBinariesModelPlugin.java    |  68 ++
 .../base/plugins/NativeBinariesPlugin.groovy       | 118 ++++
 .../nativecode/base/plugins/package-info.java      |  20 +
 .../nativecode/base/tasks/AbstractLinkTask.groovy  | 117 ++++
 .../nativecode/base/tasks/BuildBinaryTask.java     |  28 +
 .../base/tasks/CreateStaticLibrary.groovy          | 103 +++
 .../nativecode/base/tasks/InstallExecutable.groovy | 150 ++++
 .../nativecode/base/tasks/LinkExecutable.groovy    |  40 ++
 .../nativecode/base/tasks/LinkSharedLibrary.groovy |  44 ++
 .../gradle/nativecode/base/tasks/package-info.java |  20 +
 .../org/gradle/nativecode/cdt/CdtIdePlugin.groovy  |  79 +++
 .../nativecode/cdt/model/CprojectDescriptor.groovy | 117 ++++
 .../nativecode/cdt/model/CprojectSettings.groovy   | 106 +++
 .../nativecode/cdt/model/ProjectDescriptor.groovy  |  48 ++
 .../nativecode/cdt/model/ProjectSettings.groovy    |  43 ++
 .../cdt/tasks/GenerateMetadataFileTask.groovy      |  53 ++
 .../language/asm/AssemblerSourceSet.java           |  24 +
 .../language/asm/internal/AssembleSpec.java        |  38 +
 .../language/asm/internal/DefaultAssembleSpec.java |  22 +
 .../asm/internal/DefaultAssemblerSourceSet.java    |  28 +
 .../nativecode/language/asm/package-info.java      |  20 +
 .../asm/plugins/AssemblerLangPlugin.groovy         |  64 ++
 .../nativecode/language/asm/tasks/Assemble.groovy  |  89 +++
 .../base/internal/AbstractBaseCompileSpec.java     |  78 +++
 .../base/internal/AbstractBaseSourceSet.java       | 107 +++
 .../language/base/internal/NativeCompileSpec.java  |  50 ++
 .../base/tasks/AbstractNativeCompileTask.groovy    | 127 ++++
 .../gradle/nativecode/language/c/CSourceSet.java   |  28 +
 .../language/c/internal/CCompileSpec.java          |  23 +
 .../language/c/internal/DefaultCCompileSpec.java   |  31 +
 .../language/c/internal/DefaultCSourceSet.java     |  28 +
 .../gradle/nativecode/language/c/package-info.java |  20 +
 .../language/c/plugins/CLangPlugin.groovy          |  72 ++
 .../nativecode/language/c/tasks/CCompile.groovy    |  40 ++
 .../nativecode/language/cpp/CppSourceSet.java      |  28 +
 .../language/cpp/internal/CppCompileSpec.java      |  23 +
 .../cpp/internal/DefaultCppCompileSpec.java        |  31 +
 .../language/cpp/internal/DefaultCppSourceSet.java |  28 +
 .../nativecode/language/cpp/package-info.java      |  20 +
 .../cpp/plugins/CppExeConventionPlugin.groovy      |  74 ++
 .../language/cpp/plugins/CppLangPlugin.groovy      |  71 ++
 .../cpp/plugins/CppLibConventionPlugin.groovy      |  84 +++
 .../language/cpp/plugins/CppPlugin.groovy          | 125 ++++
 .../language/cpp/plugins/package-info.java         |  20 +
 .../language/cpp/tasks/CppCompile.groovy           |  40 ++
 .../language/cpp/tasks/package-info.java           |  20 +
 .../toolchain/ConfigurableToolChain.java           |  65 ++
 .../org/gradle/nativecode/toolchain/Gcc.java       |  26 +
 .../org/gradle/nativecode/toolchain/Tool.java      |  36 +
 .../org/gradle/nativecode/toolchain/VisualCpp.java |  38 +
 .../CommandLineCompilerArgumentsToOptionFile.java  |  58 ++
 .../toolchain/internal/CommandLineTool.java        |  98 +++
 .../toolchain/internal/ToolRegistry.java           |  78 +++
 .../nativecode/toolchain/internal/ToolType.java    |  42 ++
 .../internal/gpp/ArStaticLibraryArchiver.java      |  52 ++
 .../toolchain/internal/gpp/Assembler.java          |  65 ++
 .../toolchain/internal/gpp/CCompiler.java          |  50 ++
 .../toolchain/internal/gpp/CppCompiler.java        |  52 ++
 .../internal/gpp/GccCompileSourcesToArguments.java |  34 +
 .../internal/gpp/GccCompileSpecToArguments.java    |  51 ++
 .../toolchain/internal/gpp/GccToolRegistry.java    |  49 ++
 .../gpp/GeneralGccCompileOptionsToArguments.java   |  40 ++
 .../toolchain/internal/gpp/GppLinker.java          |  80 +++
 .../toolchain/internal/gpp/GppToolChain.java       | 139 ++++
 .../internal/gpp/version/GppVersionDeterminer.java | 105 +++
 .../toolchain/internal/msvcpp/Assembler.java       |  64 ++
 .../toolchain/internal/msvcpp/CCompiler.java       |  50 ++
 .../toolchain/internal/msvcpp/CppCompiler.java     |  49 ++
 .../GeneralVisualCppCompileSpecToArguments.java    |  44 ++
 .../msvcpp/LibExeStaticLibraryArchiver.java        |  53 ++
 .../toolchain/internal/msvcpp/LinkExeLinker.java   |  63 ++
 .../internal/msvcpp/VisualCppToolChain.java        | 127 ++++
 .../internal/msvcpp/VisualStudioInstall.java       |  76 ++
 .../gradle/nativecode/toolchain/package-info.java  |  20 +
 .../toolchain/plugins/GppCompilerPlugin.groovy     |  57 ++
 .../plugins/MicrosoftVisualCppPlugin.groovy        |  60 ++
 .../nativecode/toolchain/plugins/package-info.java |  20 +
 .../gradle/plugins/binaries/BinariesPlugin.java    |  66 --
 .../org/gradle/plugins/binaries/model/Binary.java  |  39 --
 .../gradle/plugins/binaries/model/CompileSpec.java |  51 --
 .../gradle/plugins/binaries/model/Compiler.java    |  24 -
 .../plugins/binaries/model/CompilerRegistry.java   |  31 -
 .../gradle/plugins/binaries/model/Executable.java  |  23 -
 .../binaries/model/HeaderExportingSourceSet.java   |  27 -
 .../org/gradle/plugins/binaries/model/Library.java |  27 -
 .../plugins/binaries/model/LibraryCompileSpec.java |  32 -
 .../model/NativeDependencyCapableSourceSet.java    |  25 -
 .../binaries/model/NativeDependencySet.java        |  28 -
 .../gradle/plugins/binaries/model/SourceSet.java   |  25 -
 .../binaries/model/internal/BinaryCompileSpec.java |  23 -
 .../model/internal/BinaryCompileSpecFactory.java   |  27 -
 .../model/internal/CompileSpecFactory.java         |  29 -
 .../binaries/model/internal/CompileTaskAware.java  |  23 -
 .../binaries/model/internal/CompilerAdapter.java   |  32 -
 .../ConfigurationBasedNativeDependencySet.groovy   |  86 ---
 .../binaries/model/internal/DefaultBinary.java     |  66 --
 .../model/internal/DefaultCompilerRegistry.java    |  90 ---
 .../binaries/model/internal/DefaultExecutable.java |  31 -
 .../binaries/model/internal/DefaultLibrary.java    |  76 --
 .../binaries/model/internal/package-info.java      |  20 -
 .../plugins/binaries/model/package-info.java       |  20 -
 .../org/gradle/plugins/binaries/package-info.java  |  20 -
 .../plugins/binaries/tasks/package-info.java       |  20 -
 .../org/gradle/plugins/cpp/CppCompile.groovy       |  33 -
 .../plugins/cpp/CppExeConventionPlugin.groovy      |  58 --
 .../org/gradle/plugins/cpp/CppExtension.java       |  50 --
 .../plugins/cpp/CppLibConventionPlugin.groovy      |  68 --
 .../groovy/org/gradle/plugins/cpp/CppPlugin.groovy |  99 ---
 .../org/gradle/plugins/cpp/CppSourceSet.java       |  65 --
 .../org/gradle/plugins/cpp/cdt/CdtIdePlugin.groovy |  77 --
 .../cpp/cdt/model/CprojectDescriptor.groovy        | 115 ---
 .../plugins/cpp/cdt/model/CprojectSettings.groovy  | 107 ---
 .../plugins/cpp/cdt/model/ProjectDescriptor.groovy |  46 --
 .../plugins/cpp/cdt/model/ProjectSettings.groovy   |  40 --
 .../cpp/cdt/tasks/GenerateMetadataFileTask.groovy  |  51 --
 .../cpp/compiler/capability/AgainstLibrary.java    |  31 -
 .../cpp/compiler/capability/CompilesCpp.java       |  30 -
 .../compiler/capability/StandardCppCompiler.java   |  23 -
 .../cpp/compiler/capability/package-info.java      |  20 -
 .../compiler/internal/CommandLineCppCompiler.java  |  68 --
 .../internal/CommandLineCppCompilerAdapter.java    |  59 --
 ...ommandLineCppCompilerArgumentsToOptionFile.java |  56 --
 .../plugins/cpp/compiler/internal/CppCompiler.java |  24 -
 .../gradle/plugins/cpp/gpp/GppCompileSpec.groovy   | 218 ------
 .../plugins/cpp/gpp/GppCompilerPlugin.groovy       |  53 --
 .../plugins/cpp/gpp/GppLibraryCompileSpec.groovy   |  40 --
 .../cpp/gpp/internal/GppCompileSpecFactory.java    |  42 --
 .../gpp/internal/GppCompileSpecToArguments.java    |  55 --
 .../plugins/cpp/gpp/internal/GppCompiler.java      |  44 --
 .../cpp/gpp/internal/GppCompilerAdapter.java       | 106 ---
 .../gpp/internal/version/GppVersionDeterminer.java | 105 ---
 .../org/gradle/plugins/cpp/gpp/package-info.java   |  20 -
 .../plugins/cpp/internal/CppCompileSpec.java       |  33 -
 .../plugins/cpp/internal/DefaultCppSourceSet.java  |  90 ---
 .../cpp/msvcpp/MicrosoftVisualCppPlugin.groovy     |  61 --
 .../internal/VisualCppCompileSpecToArguments.java  |  47 --
 .../cpp/msvcpp/internal/VisualCppCompiler.java     |  36 -
 .../msvcpp/internal/VisualCppCompilerAdapter.java  |  51 --
 .../org/gradle/plugins/cpp/package-info.java       |  20 -
 .../META-INF/gradle-plugins/binaries.properties    |   2 +-
 .../META-INF/gradle-plugins/cpp-exe.properties     |   2 +-
 .../META-INF/gradle-plugins/cpp-lib.properties     |   2 +-
 .../META-INF/gradle-plugins/cpp.properties         |   2 +-
 .../META-INF/gradle-plugins/eclipse-cdt.properties |   2 +-
 .../gradle-plugins/gpp-compiler.properties         |   2 +-
 .../META-INF/gradle-plugins/visual-cpp.properties  |   1 +
 .../cdt/model/defaultCproject-linux.xml            |   0
 .../cdt/model/defaultCproject-macos.xml            |   0
 .../cdt/model/defaultProject.xml                   |   0
 .../internal/DefaultExecutableBinaryTest.groovy    |  41 ++
 .../base/internal/DefaultExecutableTest.groovy     |  28 +
 .../internal/DefaultFlavorContainerTest.groovy     |  60 ++
 .../base/internal/DefaultFlavorTest.groovy         |  38 +
 .../internal/DefaultLibraryResolverTest.groovy     | 128 ++++
 .../base/internal/DefaultLibraryTest.groovy        |  62 ++
 .../base/internal/DefaultNativeBinaryTest.groovy   | 127 ++++
 .../internal/DefaultNativeComponentTest.groovy     |  77 ++
 .../internal/DefaultSharedLibraryBinaryTest.groovy |  70 ++
 .../internal/DefaultStaticLibraryBinaryTest.groovy |  61 ++
 .../internal/DefaultToolChainRegistryTest.groovy   | 176 +++++
 .../base/internal/NativeBinaryFactoryTest.groovy   |  97 +++
 .../internal/SourceSetNotationParserTest.groovy    |  52 ++
 .../base/plugins/NativeBinariesPluginTest.groovy   | 115 +++
 .../cdt/model/CprojectSettingsSpec.groovy          |  58 ++
 .../cdt/model/ProjectDescriptorSpec.groovy         |  48 ++
 .../cpp/internal/DefaultCppSourceSetTest.groovy    |  65 ++
 .../cpp/plugins/CppExeConventionPluginTest.groovy  |  37 +
 .../cpp/plugins/CppLibConventionPluginTest.groovy  |  34 +
 .../language/cpp/plugins/CppPluginTest.groovy      | 235 +++++++
 .../toolchain/internal/gpp/GppToolChainTest.groovy |  67 ++
 .../gpp/version/GppVersionDeterminerTest.groovy    | 142 ++++
 .../internal/msvcpp/VisualCppToolChainTest.groovy  | 127 ++++
 .../plugins/MicrosoftVisualCppPluginTest.groovy    |  63 ++
 .../internal/DefaultCompilerRegistryTest.groovy    | 130 ----
 .../plugins/cpp/CppExeConventionPluginTest.groovy  |  37 -
 .../plugins/cpp/CppLibConventionPluginTest.groovy  |  39 --
 .../org/gradle/plugins/cpp/CppPluginTest.groovy    | 235 -------
 .../cpp/cdt/model/CprojectSettingsSpec.groovy      |  58 --
 .../cpp/cdt/model/ProjectDescriptorSpec.groovy     |  48 --
 .../plugins/cpp/gpp/GppCompileSpecTest.groovy      |  40 --
 .../cpp/gpp/GppLibraryCompileSpecTest.groovy       |  37 -
 .../version/GppVersionDeterminerTest.groovy        | 142 ----
 subprojects/diagnostics/diagnostics.gradle         |   2 +-
 ...pendencyInsightReportTaskIntegrationTest.groovy | 146 +++-
 .../DependencyReportTaskIntegrationTest.groovy     |  10 +-
 .../ResolutionResultApiIntegrationTest.groovy      |  79 ---
 .../TaskReportTaskIntegrationTest.groovy           |   2 +-
 .../org/gradle/api/plugins/HelpTasksPlugin.groovy  |  23 +-
 .../gradle/api/plugins/ProjectReportsPlugin.java   |   8 +-
 .../plugins/internal/HelpTasksAutoApplyAction.java |  26 +
 .../api/tasks/diagnostics/AbstractReportTask.java  |   2 +-
 .../diagnostics/DependencyInsightReportTask.groovy |   2 +-
 .../tasks/diagnostics/DependencyReportTask.java    |   2 -
 .../api/tasks/diagnostics/ProjectReportTask.java   |   2 +-
 .../internal/DependencyReportRenderer.java         |   2 -
 .../tasks/diagnostics/internal/GraphRenderer.java  |  69 --
 .../internal/SingleProjectTaskReportModel.java     |   6 +-
 .../diagnostics/internal/TaskReportRenderer.java   |   2 -
 .../AsciiDependencyReportRenderer.java             |   4 +-
 .../internal/dsl/DependencyResultSpec.java         |   3 -
 .../dsl/DependencyResultSpecNotationParser.java    |   3 -
 .../internal/graph/DependencyGraphRenderer.groovy  |   5 +-
 .../diagnostics/internal/graph/NodeRenderer.groovy |   3 -
 .../internal/graph/SimpleNodeRenderer.java         |   3 -
 .../nodes/AbstractRenderableModuleResult.java      |   3 -
 .../nodes/InvertedRenderableModuleResult.java      |   2 -
 .../internal/graph/nodes/RenderableDependency.java |   3 -
 .../graph/nodes/RenderableDependencyResult.java    |   3 -
 .../graph/nodes/RenderableModuleResult.java        |   3 -
 .../insight/DependencyInsightReporter.groovy       |   2 -
 .../internal/insight/DependencyResultSorter.java   |  27 +-
 ...le.configuration.project.ProjectConfigureAction |   1 +
 .../gradle/api/plugins/HelpTasksPluginSpec.groovy  |  26 +-
 .../api/plugins/ProjectReportsPluginTest.java      |   4 +-
 .../ReportingBasePluginConventionTest.groovy       |  64 --
 .../api/plugins/ReportingBasePluginTest.groovy     |  46 --
 .../tasks/diagnostics/AbstractReportTaskTest.java  |   8 +-
 .../DependencyInsightReportTaskSpec.groovy         |  15 +-
 .../diagnostics/DependencyReportTaskTest.groovy    |  19 +-
 .../tasks/diagnostics/ProjectReportTaskTest.groovy |  14 +-
 .../tasks/diagnostics/PropertyReportTaskTest.java  |   4 +-
 .../api/tasks/diagnostics/TaskReportTaskTest.java  |   4 +-
 .../internal/TaskReportRendererTest.groovy         |   3 -
 .../AsciiDependencyReportRendererTest.groovy       |   4 +-
 .../DependencyResultSpecNotationParserSpec.groovy  |   5 +-
 .../internal/dsl/DependencyResultSpecTest.groovy   |   4 -
 .../graph/DependencyGraphRendererSpec.groovy       |   5 +-
 .../AbstractRenderableDependencyResultSpec.groovy  |   3 -
 .../nodes/RenderableDependencyResultTest.groovy    |   3 -
 .../internal/graph/nodes/SimpleDependency.java     |   0
 .../insight/DependencyInsightReporterSpec.groovy   |   6 +-
 .../insight/DependencyResultSorterSpec.groovy      |  52 +-
 subprojects/distributions/distributions.gradle     |  14 +-
 .../gradle/AllDistributionIntegrationSpec.groovy   |   2 +-
 .../org/gradle/DistributionIntegrationSpec.groovy  |   7 +-
 subprojects/docs/docs.gradle                       |  46 +-
 subprojects/docs/src/docs/css/release-notes.css    |  29 +-
 subprojects/docs/src/docs/dsl/dsl.xml              |  89 ++-
 .../docs/src/docs/dsl/org.gradle.api.Task.xml      |  12 +
 ....gradle.api.artifacts.dsl.DependencyHandler.xml |   6 +
 ....gradle.api.artifacts.dsl.RepositoryHandler.xml |   3 +
 ...dle.api.plugins.ApplicationPluginConvention.xml |   6 +-
 ...adle.api.plugins.jetty.AbstractJettyRunTask.xml |   8 +
 .../org.gradle.api.plugins.jetty.JettyRunWar.xml   |   8 +
 .../org.gradle.api.plugins.quality.CodeNarc.xml    |  12 +
 ...radle.api.plugins.quality.CodeNarcExtension.xml |  15 +
 ...org.gradle.api.publish.PublicationContainer.xml |   3 -
 .../org.gradle.api.publish.ivy.IvyPublication.xml  |  24 +
 ...g.gradle.api.publish.maven.MavenPublication.xml |   9 +
 ...gradle.api.reporting.GenerateBuildDashboard.xml |   3 -
 .../docs/dsl/org.gradle.api.reporting.Report.xml   |  37 +
 .../org.gradle.api.reporting.ReportContainer.xml   |  25 +
 .../dsl/org.gradle.api.reporting.Reporting.xml     |  28 +
 ...org.gradle.api.reporting.ReportingExtension.xml |  28 +
 .../dsl/org.gradle.api.tasks.AbstractCopyTask.xml  |  10 +
 .../src/docs/dsl/org.gradle.api.tasks.Copy.xml     |   2 +-
 .../dsl/org.gradle.api.tasks.GroovyRuntime.xml     |  25 +
 .../docs/dsl/org.gradle.api.tasks.ScalaRuntime.xml |  31 +
 ...le.api.tasks.application.CreateStartScripts.xml |   3 +-
 .../org.gradle.api.tasks.compile.GroovyCompile.xml |   2 +-
 ...api.tasks.incremental.IncrementalTaskInputs.xml |  25 +
 .../org.gradle.api.tasks.incremental.InputFile.xml |  26 +
 .../org.gradle.api.tasks.scala.ScalaCompile.xml    |   2 +-
 .../dsl/org.gradle.api.tasks.scala.ScalaDoc.xml    |   2 +-
 .../docs/dsl/org.gradle.api.tasks.testing.Test.xml |  11 +
 .../dsl/org.gradle.api.tasks.wrapper.Wrapper.xml   |   4 +-
 .../dsl/org.gradle.buildsetup.tasks.SetupBuild.xml |  43 ++
 .../org.gradle.language.base.BinaryContainer.xml   |  38 +
 .../dsl/org.gradle.nativecode.base.Executable.xml  |  38 +
 ...org.gradle.nativecode.base.ExecutableBinary.xml |  38 +
 ....gradle.nativecode.base.ExecutableContainer.xml |  38 +
 .../dsl/org.gradle.nativecode.base.Library.xml     |  47 ++
 .../org.gradle.nativecode.base.LibraryBinary.xml   |  41 ++
 ...org.gradle.nativecode.base.LibraryContainer.xml |  38 +
 .../org.gradle.nativecode.base.NativeBinary.xml    |  77 ++
 .../org.gradle.nativecode.base.NativeComponent.xml |  56 ++
 ....gradle.nativecode.base.SharedLibraryBinary.xml |  38 +
 ....gradle.nativecode.base.StaticLibraryBinary.xml |  38 +
 .../dsl/org.gradle.nativecode.base.ToolChain.xml   |  38 +
 ...rg.gradle.nativecode.base.ToolChainRegistry.xml |  44 ++
 ...adle.nativecode.base.tasks.AbstractLinkTask.xml |  59 ++
 ...e.nativecode.base.tasks.CreateStaticLibrary.xml |  50 ++
 ...dle.nativecode.base.tasks.InstallExecutable.xml |  50 ++
 ...gradle.nativecode.base.tasks.LinkExecutable.xml |  38 +
 ...dle.nativecode.base.tasks.LinkSharedLibrary.xml |  38 +
 ...adle.nativecode.language.asm.tasks.Assemble.xml |  47 ++
 ...nguage.base.tasks.AbstractNativeCompileTask.xml |  56 ++
 ...gradle.nativecode.language.c.tasks.CCompile.xml |  38 +
 ...language.cpp.plugins.CppExeConventionPlugin.xml |  38 +
 ...ativecode.language.cpp.plugins.CppExtension.xml |  41 ++
 ...language.cpp.plugins.CppLibConventionPlugin.xml |  38 +
 ...e.nativecode.language.cpp.plugins.CppPlugin.xml |  38 +
 ...le.nativecode.language.cpp.tasks.CppCompile.xml |  38 +
 ...ivecode.toolchain.plugins.GppCompilerPlugin.xml |  38 +
 ....toolchain.plugins.MicrosoftVisualCppPlugin.xml |  38 +
 ...g.gradle.plugins.ide.idea.model.IdeaProject.xml |   2 +-
 ...esting.jacoco.plugins.JacocoPluginExtension.xml |  50 ++
 ....testing.jacoco.plugins.JacocoTaskExtension.xml |  77 ++
 .../org.gradle.testing.jacoco.tasks.JacocoBase.xml |  45 ++
 ...org.gradle.testing.jacoco.tasks.JacocoMerge.xml |  50 ++
 ...rg.gradle.testing.jacoco.tasks.JacocoReport.xml |  69 ++
 subprojects/docs/src/docs/dsl/plugins.xml          |  10 +
 .../docs/src/docs/release/content/script.js        |  26 +-
 .../docs/src/docs/release/notes-template.md        |  14 +-
 subprojects/docs/src/docs/release/notes.md         | 557 +++++++--------
 subprojects/docs/src/docs/stylesheets/dslHtml.xsl  |  10 +-
 .../src/docs/stylesheets/userGuideHtmlCommon.xsl   |   2 +-
 .../docs/src/docs/userguide/artifactMngmt.xml      |   4 +-
 .../docs/src/docs/userguide/bootstrapPlugin.xml    |  88 ---
 .../docs/userguide/buildAnnouncementsPlugin.xml    |   2 +-
 .../src/docs/userguide/buildDashboardPlugin.xml    |  20 +-
 .../docs/src/docs/userguide/buildLifecycle.xml     |   6 +-
 .../docs/src/docs/userguide/buildSetupPlugin.xml   | 177 +++++
 .../docs/src/docs/userguide/comparingBuilds.xml    |   4 +-
 subprojects/docs/src/docs/userguide/cpp.xml        | 174 ++++-
 .../docs/src/docs/userguide/customTasks.xml        | 142 ++++
 subprojects/docs/src/docs/userguide/depMngmt.xml   | 144 ++--
 .../docs/src/docs/userguide/distributionPlugin.xml | 206 ++++--
 .../docs/src/docs/userguide/eclipsePlugin.xml      |   4 +-
 .../docs/src/docs/userguide/gradleDaemon.xml       |   4 +-
 .../docs/src/docs/userguide/gradleWrapper.xml      |  64 +-
 .../docs/src/docs/userguide/groovyPlugin.xml       |  70 +-
 .../docs/src/docs/userguide/groovyTutorial.xml     |   4 +-
 subprojects/docs/src/docs/userguide/ideSupport.xml |   2 +-
 subprojects/docs/src/docs/userguide/ideaPlugin.xml |   4 +-
 .../src/docs/userguide/img/jacocoHtmlReport.png    | Bin 0 -> 103803 bytes
 .../docs/src/docs/userguide/initscripts.xml        |  19 +-
 .../docs/src/docs/userguide/jacocoPlugin.xml       | 248 +++++++
 .../userguide/javaLibraryDistributionPlugin.xml    |   3 +-
 subprojects/docs/src/docs/userguide/javaPlugin.xml |  38 +-
 .../docs/src/docs/userguide/javaTutorial.xml       |   8 +-
 .../docs/src/docs/userguide/mavenPlugin.xml        |   2 +-
 .../docs/src/docs/userguide/multiproject.xml       |   4 +-
 .../docs/src/docs/userguide/organizeBuildLogic.xml |   2 +-
 .../docs/src/docs/userguide/publishingIvy.xml      |  47 +-
 .../docs/src/docs/userguide/publishingMaven.xml    |  33 +-
 .../docs/src/docs/userguide/scalaPlugin.xml        |  35 +-
 .../docs/src/docs/userguide/sonarPlugin.xml        |  40 +-
 .../docs/src/docs/userguide/sonarRunnerPlugin.xml  |   4 +-
 .../docs/src/docs/userguide/standardPlugins.xml    | 170 +++--
 subprojects/docs/src/docs/userguide/tasks.xml      |  84 ++-
 .../docs/src/docs/userguide/thisAndThat.xml        |   3 +-
 subprojects/docs/src/docs/userguide/userguide.xml  |   6 +-
 subprojects/docs/src/docs/userguide/warPlugin.xml  |   2 +-
 .../docs/src/docs/userguide/workingWithFiles.xml   |  41 +-
 .../docs/src/docs/userguide/wrapperPlugin.xml      |  59 ++
 .../src/docs/userguide/writingBuildScripts.xml     |   2 +-
 .../docs/src/samples/buildDashboard/build.gradle   |   2 +-
 .../src/main/java/org/gradle/sample/Person.java    |  15 +
 .../docs/src/samples/codeQuality/build.gradle      |   2 +-
 .../src/samples/cpp/c-with-assembler/build.gradle  |  46 ++
 .../c-with-assembler/src/main/asm_i386_gcc/sum.s   |   6 +
 .../c-with-assembler/src/main/asm_i386_masm/sum.s  |  12 +
 .../c-with-assembler/src/main/asm_i386_osx/sum.s   |  10 +
 .../c-with-assembler/src/main/asm_x64_gcc/sum.s    |   7 +
 .../samples/cpp/c-with-assembler/src/main/c/main.c |   8 +
 .../cpp/c-with-assembler/src/main/headers/sum.h    |   1 +
 subprojects/docs/src/samples/cpp/c/build.gradle    |  80 +++
 .../src/main/cpp/main.cpp => c/src/exe/c/main.c}   |   0
 .../docs/src/samples/cpp/c/src/lib/c/hello.c       |   6 +
 .../docs/src/samples/cpp/c/src/lib/headers/hello.h |   8 +
 .../docs/src/samples/cpp/cpp-exe/build.gradle      |  33 +
 .../docs/src/samples/cpp/cpp-exe/settings.gradle   |   1 +
 .../cpp/{exe => cpp-exe}/src/main/cpp/hello.cpp    |   0
 .../docs/src/samples/cpp/cpp-lib/build.gradle      |  14 +
 .../docs/src/samples/cpp/cpp-lib/settings.gradle   |   1 +
 .../src/samples/cpp/cpp-lib/src/main/cpp/hello.cpp |  14 +
 .../lib => cpp-lib}/src/main/headers/hello.h       |   0
 subprojects/docs/src/samples/cpp/cpp/build.gradle  |  64 ++
 .../exe/src/main => cpp/src/exe}/cpp/main.cpp      |   0
 .../docs/src/samples/cpp/cpp/src/lib/cpp/hello.cpp |   6 +
 .../src/samples/cpp/cpp/src/lib/headers/hello.h    |   7 +
 .../docs/src/samples/cpp/dependencies/build.gradle |  12 +-
 subprojects/docs/src/samples/cpp/exe/build.gradle  |  11 -
 .../docs/src/samples/cpp/exewithlib/build.gradle   |  18 -
 .../src/samples/cpp/multi-project/build.gradle     |  18 +
 .../exe/src/main/cpp/main.cpp                      |   0
 .../lib/src/main/cpp/hello.cpp                     |   0
 .../lib/src/main/headers/hello.h                   |   0
 .../{exewithlib => multi-project}/settings.gradle  |   0
 .../docs/src/samples/cpp/variants/build.gradle     |  39 ++
 .../exe/src/main => variants/src/exe}/cpp/main.cpp |   0
 .../src/samples/cpp/variants/src/lib/cpp/hello.cpp |  10 +
 .../samples/cpp/variants/src/lib/headers/hello.h   |  10 +
 .../groovy/org/gradle/samples/ProductPlugin.groovy |   2 +-
 .../samples/customDistribution/plugin/build.gradle |   2 +-
 .../src/samples/customPlugin/plugin/build.gradle   |   2 +-
 .../main/groovy/org/gradle/GroovyJavaPerson.java   |   3 -
 .../src/main/groovy/org/gradle/GroovyPerson.groovy |   3 -
 .../src/main/java/org/gradle/JavaPerson.java       |   3 -
 .../descriptor-customization/build.gradle          |   4 +-
 .../ivy-publish/java-multi-project/build.gradle    |   2 +-
 .../ivy-publish/multiple-publications/build.gradle |  83 +++
 .../multiple-publications/output/project1.ivy.xml  |  15 +
 .../output/project2-api.ivy.xml                    |  13 +
 .../output/project2-impl.ivy.xml                   |  16 +
 .../multiple-publications/settings.gradle          |   2 +
 .../samples/ivy-publish/quickstart/build.gradle    |   2 +-
 .../java/org/gradle/webservice/TestTestTest.java   |   3 -
 .../docs/src/samples/java/quickstart/build.gradle  |   8 +-
 .../samples/maven-publish/javaProject/build.gradle |   2 +-
 .../multiple-publications/build.gradle             |  69 ++
 .../multiple-publications/output/project1.pom.xml  |  17 +
 .../output/project2-api.pom.xml                    |   9 +
 .../output/project2-impl.pom.xml                   |  23 +
 .../multiple-publications/settings.gradle          |   2 +
 .../maven-publish/pomCustomization/build.gradle    |   4 +-
 .../samples/maven-publish/quickstart/build.gradle  |   2 +-
 .../multiProjectBuildSrc/buildSrc/build.gradle     |   2 +-
 subprojects/docs/src/samples/osgi/build.gradle     |   4 +-
 .../main/groovy/org/gradle/GradleActivator.groovy  |   5 -
 .../samples/sonarRunner/multiProject/build.gradle  |   4 +-
 .../samples/sonarRunner/quickstart/build.gradle    |   4 +-
 .../src/main => src/main/cpp/library}/cpp/main.cpp |   0
 .../testing/jacoco/application/build.gradle        |  35 +
 .../src/main/java/org/gradle/MyMain.java           |  30 +
 .../samples/testing/jacoco/quickstart/build.gradle |  58 ++
 .../src/main/java/org/gradle/Person.java           |  24 +
 .../src/test/java/org/gradle/PersonTest.java       |  41 ++
 .../samples/testing/junit/categories/build.gradle  |  18 +
 .../org/gradle/junit/CategorizedJUnitTest.java     |  35 +
 .../src/test/java/org/gradle/junit/CategoryA.java  |  21 +
 .../src/test/java/org/gradle/junit/CategoryB.java  |  21 +
 .../java/org/gradle/junit/SimpleJUnitTest.java     |  27 +
 .../src/samples/testing/testReport/build.gradle    |   2 +-
 .../src/samples/testing/testng/groups/build.gradle |  18 +
 .../org/gradle/testng/SimpleIntegrationTest.java   |  29 +
 .../java/org/gradle/testng/SimpleUnitTest.java     |  29 +
 .../src/main/java/org/gradle/testng/User.java      |   3 -
 .../src/main/java/org/gradle/testng/UserImpl.java  |   3 -
 .../test/java/org/gradle/testng/UserImplTest.java  |   3 -
 .../toolingApi/customModel/plugin/build.gradle     |  20 +
 .../java/org/gradle/sample/plugin/CustomModel.java |  14 +
 .../org/gradle/sample/plugin/CustomPlugin.java     |  39 ++
 .../org/gradle/sample/plugin/DefaultModel.java     |  20 +
 .../gradle-plugins/custom-plugin.properties        |   1 +
 .../src/samples/toolingApi/customModel/readme.xml  |   3 +
 .../customModel/sampleBuild/build.gradle           |  10 +
 .../customModel/sampleBuild/settings.gradle        |  16 +
 .../toolingApi/customModel/tooling/build.gradle    |  22 +
 .../src/main/java/org/gradle/sample/Main.java      |  39 ++
 .../src/main/java/org/gradle/sample/Main.java      |   5 +-
 .../idea/src/main/java/org/gradle/sample/Main.java |   3 +-
 .../src/main/java/org/gradle/sample/Main.java      |   5 +-
 .../src/main/java/org/gradle/sample/Main.java      |   3 -
 .../artifacts/componentMetadata/build.gradle       |  41 ++
 .../repo/air.birds/albatros/1.9/ivy-1.9.xml        |   9 +
 .../repo/air.birds/albatros/2.0/ivy-2.0.xml        |   9 +
 .../repo/sea.fish/tuna/1.3/ivy-1.3.xml             |   9 +
 .../repo/sea.fish/tuna/1.4/ivy-1.4.xml             |   9 +
 .../repo/sea.fish/tuna/1.5/ivy-1.5.xml             |   9 +
 .../artifacts/defineRepository/build.gradle        |   6 +
 .../userguide/artifacts/uploading/build.gradle     |   4 +-
 .../samples/userguide/distribution/build.gradle    |  36 +-
 .../src/samples/userguide/files/copy/build.gradle  |  15 +
 .../userguide/groovy/groovyDependency/build.gradle |   6 -
 .../userguide/initScripts/plugins/build.gradle     |  27 +
 .../userguide/initScripts/plugins/init.gradle      |  34 +
 .../firstMessages/messages/build.gradle            |   1 +
 .../firstMessages/messages/consumer/build.gradle   |   3 +-
 .../messages/build.gradle                          |   1 +
 .../messages/consumer/build.gradle                 |   2 +-
 .../messages/build.gradle                          |   1 +
 .../messages/consumer/build.gradle                 |   3 +-
 .../messages/build.gradle                          |   1 +
 .../messages/consumer/build.gradle                 |   2 +-
 .../messagesHack/messages/build.gradle             |   1 +
 .../messagesHack/messages/consumer/build.gradle    |   3 +-
 .../messagesTaskDependencies/messages/build.gradle |   1 +
 .../messages/consumer/build.gradle                 |   3 +-
 .../messagesWithDependencies/messages/build.gradle |   1 +
 .../messages/consumer/build.gradle                 |   3 +-
 .../multiproject/standardLayouts/settings.gradle   |   2 +-
 .../tasks/addToTaskContainer/build.gradle          |   4 +-
 .../tasks/customTaskWithProperty/build.gradle      |   2 +-
 .../userguide/tasks/finalizers/build.gradle        |   8 +
 .../tasks/finalizersWithFailure/build.gradle       |   9 +
 .../userguide/tasks/incrementalTask/build.gradle   |  66 ++
 .../userguide/tasks/mustRunAfter/build.gradle      |   7 +
 .../tutorial/groovyWithFlatDir/build.gradle        |  10 +-
 .../userguide/wrapper/customized/build.gradle      |   4 -
 .../samples/userguide/wrapper/simple/build.gradle  |   2 +-
 .../configurationHandlingAllFiles.out              |   4 +-
 .../samples/userguideOutput/customStatusScheme.out |   1 +
 .../userguideOutput/externalDependencies.out       |   4 +-
 .../incrementalTaskChangedProperty.out             |   4 +
 .../userguideOutput/incrementalTaskFirstRun.out    |   4 +
 .../incrementalTaskNoChange.out}                   |   0
 .../incrementalTaskRemovedInput.out                |   2 +
 .../incrementalTaskRemovedOutput.out               |   4 +
 .../incrementalTaskUpdatedInputs.out               |   3 +
 .../src/samples/userguideOutput/latestSelector.out |   3 +
 .../src/samples/userguideOutput/mustRunAfter.out   |   2 +
 .../userguideOutput/mustRunAfterSingleTask.out     |   1 +
 .../publishingIvyGenerateDescriptor.out            |   2 +-
 .../publishingIvyPublishLifecycle.out              |   2 +-
 .../userguideOutput/publishingIvyPublishSingle.out |   2 +-
 .../src/samples/userguideOutput/taskFinalizers.out |   2 +
 .../userguideOutput/taskFinalizersWithFailure.out  |   2 +
 .../samples/userguideOutput/taskListAllReport.out  |   5 +
 .../src/samples/userguideOutput/taskListReport.out |   5 +
 .../userguideOutput/usePluginsInInitScripts.out    |   1 +
 .../samples/webApplication/customised/readme.xml   |   3 -
 .../{customised => customized}/build.gradle        |   0
 .../samples/webApplication/customized/readme.xml   |   3 +
 .../src/additionalWebInf/additional.xml            |   0
 .../src/main/java/org/gradle/HelloServlet.java     |   0
 .../src/main/java/org/gradle/MyClass.java          |   0
 .../src/main/webapp/WEB-INF/webapp.xml             |   0
 .../src/main/webapp/webapp.html                    |   0
 .../src/rootContent/root.txt                       |   0
 .../{customised => customized}/src/someWeb.xml     |   0
 .../src/test/java/org/gradle/MyClassTest.java      |   0
 .../releasenotes/FunctionalReleaseNotesTest.groovy |   6 +-
 .../docs/src/transforms/release-notes.gradle       |  36 +-
 subprojects/ear/ear.gradle                         |   1 -
 .../plugins/ear/EarPluginIntegrationTest.groovy    |  84 ++-
 .../main/groovy/org/gradle/plugins/ear/Ear.groovy  |  31 +-
 .../groovy/org/gradle/plugins/ear/EarPlugin.java   |   8 +-
 .../ear/descriptor/DeploymentDescriptor.java       |   2 -
 .../gradle/plugins/ear/descriptor/EarModule.java   |   2 -
 .../plugins/ear/descriptor/EarSecurityRole.java    |   2 -
 .../plugins/ear/descriptor/EarWebModule.java       |   2 -
 .../internal/DefaultDeploymentDescriptor.groovy    |   3 -
 .../descriptor/internal/DefaultEarModule.groovy    |   3 -
 .../internal/DefaultEarSecurityRole.groovy         |   3 -
 .../descriptor/internal/DefaultEarWebModule.groovy |   3 -
 .../org/gradle/plugins/ear/EarPluginTest.groovy    |  16 +-
 .../groovy/org/gradle/plugins/ear/EarTest.groovy   |   8 +-
 .../DefaultDeploymentDescriptorTest.groovy         |   3 -
 subprojects/ide/ide.gradle                         |   2 +-
 .../ide/AutoTestedSamplesIntegrationTest.groovy    |   3 -
 .../eclipse/EclipseClasspathIntegrationTest.groovy |   3 -
 .../ide/eclipse/EclipseEarIntegrationTest.groovy   |   3 -
 .../EclipseMultiModuleIntegrationTest.groovy       |   3 -
 .../eclipse/EclipseProjectIntegrationTest.groovy   |   3 -
 .../eclipse/EclipseWtpModelIntegrationTest.groovy  |   3 -
 .../plugins/ide/idea/IdeaIntegrationTest.groovy    |  74 +-
 .../ide/idea/IdeaMultiModuleIntegrationTest.groovy |   3 -
 .../ide/idea/IdeaWorkspaceIntegrationTest.groovy   |   3 -
 .../expectedFiles/project1/project1.iml.xml        |  32 -
 .../expectedFiles/project2/project2.iml.xml        |  32 -
 .../expectedFiles/project3/project3.iml.xml        |  32 -
 .../expectedFiles/root.ipr.xml                     | 123 ----
 .../plugins/ide/api/FileContentMerger.groovy       |   2 -
 .../plugins/ide/api/XmlFileContentMerger.groovy    |   4 +-
 .../plugins/ide/eclipse/EclipsePlugin.groovy       |   6 +-
 .../plugins/ide/eclipse/EclipseWtpPlugin.groovy    |   9 +-
 .../ide/eclipse/GenerateEclipseClasspath.groovy    |   2 -
 .../ide/eclipse/GenerateEclipseProject.groovy      |   2 -
 .../ide/eclipse/GenerateEclipseWtpComponent.groovy |   2 -
 .../ide/eclipse/GenerateEclipseWtpFacet.groovy     |   2 -
 .../ide/eclipse/internal/EclipseNameDeduper.groovy |   3 -
 .../eclipse/internal/LinkedResourcesCreator.groovy |   5 +-
 .../eclipse/model/AbstractClasspathEntry.groovy    |   3 -
 .../ide/eclipse/model/AbstractLibrary.groovy       |   3 -
 .../plugins/ide/eclipse/model/AccessRule.groovy    |   3 -
 .../plugins/ide/eclipse/model/BuildCommand.groovy  |   3 -
 .../plugins/ide/eclipse/model/Classpath.groovy     |   4 +-
 .../plugins/ide/eclipse/model/ClasspathEntry.java  |   2 -
 .../plugins/ide/eclipse/model/Container.groovy     |   3 -
 .../ide/eclipse/model/EclipseClasspath.groovy      |   4 +-
 .../ide/eclipse/model/EclipseDomainModel.groovy    |   2 -
 .../plugins/ide/eclipse/model/EclipseJdt.groovy    |   2 -
 .../plugins/ide/eclipse/model/EclipseModel.groovy  |   2 -
 .../ide/eclipse/model/EclipseProject.groovy        |   8 +-
 .../plugins/ide/eclipse/model/EclipseWtp.groovy    |   2 -
 .../ide/eclipse/model/EclipseWtpComponent.groovy   |   8 +-
 .../ide/eclipse/model/EclipseWtpFacet.groovy       |   6 +-
 .../gradle/plugins/ide/eclipse/model/Facet.groovy  |   3 -
 .../plugins/ide/eclipse/model/Library.groovy       |   3 -
 .../gradle/plugins/ide/eclipse/model/Link.groovy   |   3 -
 .../gradle/plugins/ide/eclipse/model/Output.groovy |   3 -
 .../plugins/ide/eclipse/model/Project.groovy       |   2 -
 .../ide/eclipse/model/ProjectDependency.groovy     |   3 -
 .../plugins/ide/eclipse/model/SourceFolder.groovy  |   2 -
 .../plugins/ide/eclipse/model/Variable.groovy      |   3 -
 .../ide/eclipse/model/WbDependentModule.groovy     |   3 -
 .../plugins/ide/eclipse/model/WbProperty.groovy    |   3 -
 .../plugins/ide/eclipse/model/WbResource.groovy    |   3 -
 .../plugins/ide/eclipse/model/WtpComponent.groovy  |   2 -
 .../plugins/ide/eclipse/model/WtpFacet.groovy      |   2 -
 .../model/internal/ClassFoldersCreator.groovy      |   2 -
 .../eclipse/model/internal/ClasspathFactory.groovy |   5 +-
 .../model/internal/ExportedEntriesUpdater.groovy   |   3 -
 .../ide/eclipse/model/internal/PathUtil.groovy     |   3 -
 .../model/internal/ProjectDependencyBuilder.groovy |   3 -
 .../model/internal/SourceFoldersCreator.groovy     |   3 -
 .../model/internal/WtpComponentFactory.groovy      |   3 -
 .../plugins/ide/idea/GenerateIdeaModule.groovy     |   2 -
 .../plugins/ide/idea/GenerateIdeaProject.groovy    |   2 -
 .../plugins/ide/idea/GenerateIdeaWorkspace.groovy  |   2 -
 .../org/gradle/plugins/ide/idea/IdeaPlugin.groovy  |   9 +-
 .../ide/idea/internal/IdeaNameDeduper.groovy       |   3 -
 .../ide/idea/internal/IdeaScalaConfigurer.groovy   |  13 +-
 .../gradle/plugins/ide/idea/model/Dependency.java  |   2 -
 .../ide/idea/model/IdeaLanguageLevel.groovy        |   2 -
 .../gradle/plugins/ide/idea/model/IdeaModel.groovy |   1 -
 .../plugins/ide/idea/model/IdeaModule.groovy       |   8 +-
 .../plugins/ide/idea/model/IdeaModuleIml.groovy    |   2 -
 .../plugins/ide/idea/model/IdeaProject.groovy      |   6 +-
 .../plugins/ide/idea/model/IdeaWorkspace.groovy    |   6 +-
 .../plugins/ide/idea/model/JarDirectory.groovy     |   2 -
 .../org/gradle/plugins/ide/idea/model/Jdk.groovy   |   2 -
 .../gradle/plugins/ide/idea/model/Module.groovy    |   2 -
 .../plugins/ide/idea/model/ModuleDependency.groovy |   4 +-
 .../plugins/ide/idea/model/ModuleLibrary.groovy    |   4 +-
 .../org/gradle/plugins/ide/idea/model/Path.groovy  |   2 -
 .../gradle/plugins/ide/idea/model/Project.groovy   |   2 -
 .../gradle/plugins/ide/idea/model/Workspace.groovy |   2 -
 .../model/internal/IdeaDependenciesProvider.groovy |   3 -
 .../model/internal/ModuleDependencyBuilder.groovy  |   3 -
 .../ide/internal/IdeDependenciesExtractor.groovy   |   5 +-
 .../gradle/plugins/ide/internal/IdePlugin.groovy   |   2 +-
 .../internal/JavadocAndSourcesDownloader.groovy    |  11 +-
 .../internal/configurer/DeduplicationTarget.groovy |   3 -
 .../internal/configurer/ModuleNameDeduper.groovy   |   1 -
 .../ide/internal/configurer/ProjectDeduper.groovy  |   3 -
 .../internal/tooling/BasicIdeaModelBuilder.java    |  33 +
 .../ide/internal/tooling/EclipseModelBuilder.java  | 151 ++++
 .../ide/internal/tooling/GradleBuildBuilder.java   |  54 ++
 .../ide/internal/tooling/GradleProjectBuilder.java |  82 +++
 .../ide/internal/tooling/IdeaModelBuilder.java     | 138 ++++
 .../plugins/ide/internal/tooling/TasksFactory.java |  46 ++
 .../tooling/ToolingRegistrationAction.java         |  32 +
 .../eclipse/DefaultEclipseExternalDependency.java  |  54 ++
 .../eclipse/DefaultEclipseLinkedResource.java      |  52 ++
 .../tooling/eclipse/DefaultEclipseProject.java     | 135 ++++
 .../eclipse/DefaultEclipseProjectDependency.java   |  44 ++
 .../eclipse/DefaultEclipseSourceDirectory.java     |  44 ++
 .../tooling/eclipse/DefaultEclipseTask.java        |  56 ++
 .../tooling/idea/DefaultIdeaCompilerOutput.java    |  65 ++
 .../tooling/idea/DefaultIdeaContentRoot.java       |  80 +++
 .../tooling/idea/DefaultIdeaDependency.java        |  22 +
 .../tooling/idea/DefaultIdeaDependencyScope.java   |  64 ++
 .../tooling/idea/DefaultIdeaLanguageLevel.java     |  82 +++
 .../internal/tooling/idea/DefaultIdeaModule.java   | 120 ++++
 .../tooling/idea/DefaultIdeaModuleDependency.java  |  61 ++
 .../internal/tooling/idea/DefaultIdeaProject.java  | 107 +++
 .../DefaultIdeaSingleEntryLibraryDependency.java   |  97 +++
 .../tooling/idea/DefaultIdeaSourceDirectory.java   |  43 ++
 .../internal/idea/DefaultIdeaModuleDependency.java |  27 +
 .../DefaultIdeaSingleEntryLibraryDependency.java   |  27 +
 .../internal/provider/BasicIdeaModelBuilder.java   |  36 -
 .../internal/provider/BuildModelAction.java        |  88 ---
 .../tooling/internal/provider/BuildsModel.java     |  28 -
 .../internal/provider/EclipseModelBuilder.java     | 162 -----
 .../internal/provider/FileOutcomeIdentifier.java   |  41 --
 .../internal/provider/GradleProjectBuilder.java    |  80 ---
 .../internal/provider/IdeaModelBuilder.java        | 144 ----
 .../internal/provider/NullResultBuilder.java       |  30 -
 .../provider/ProjectOutcomesModelBuilder.java      |  71 --
 ...blishArtifactToFileBuildOutcomeTransformer.java |  98 ---
 .../tooling/internal/provider/TasksFactory.java    |  46 --
 ...le.configuration.project.ProjectConfigureAction |   1 +
 .../plugins/ide/eclipse/EclipsePluginTest.groovy   |   7 +-
 .../ide/eclipse/EclipseWtpPluginTest.groovy        |   7 +-
 .../eclipse/GenerateEclipseClasspathTest.groovy    |   3 -
 .../eclipse/GenerateEclipseWtpComponentTest.groovy |   3 -
 .../ide/eclipse/GenerateEclipseWtpFacetTest.groovy |   3 -
 .../plugins/ide/eclipse/model/ClasspathTest.groovy |   3 -
 .../plugins/ide/eclipse/model/ContainerTest.groovy |   3 -
 .../ide/eclipse/model/EclipseModelTest.groovy      |   3 -
 .../ide/eclipse/model/EclipseProjectTest.groovy    |   3 -
 .../plugins/ide/eclipse/model/FacetTest.groovy     |   7 +-
 .../plugins/ide/eclipse/model/LibraryTest.groovy   |   3 -
 .../plugins/ide/eclipse/model/OutputTest.groovy    |   3 -
 .../ide/eclipse/model/ProjectDependencyTest.groovy |   3 -
 .../plugins/ide/eclipse/model/ProjectTest.groovy   |   3 -
 .../ide/eclipse/model/SourceFolderTest.groovy      |   3 -
 .../plugins/ide/eclipse/model/VariableTest.groovy  |   3 -
 .../ide/eclipse/model/WbDependentModuleTest.groovy |   3 -
 .../ide/eclipse/model/WbPropertyTest.groovy        |   3 -
 .../ide/eclipse/model/WbResourceTest.groovy        |   3 -
 .../ide/eclipse/model/WtpComponentTest.groovy      |   3 -
 .../plugins/ide/eclipse/model/WtpFacetTest.groovy  |   3 -
 .../internal/ProjectDependencyBuilderTest.groovy   |   7 +-
 .../ide/idea/ GenerateIdeaModuleTest.groovy        |  11 +-
 .../gradle/plugins/ide/idea/IdeaPluginTest.groovy  |  25 +-
 .../ide/idea/model/IdeaLanguageLevelTest.groovy    |   3 -
 .../ide/idea/model/ModuleDependencyTest.groovy     |   3 -
 .../ide/idea/model/ModuleLibraryTest.groovy        |   3 -
 .../plugins/ide/idea/model/ModuleTest.groovy       |   3 -
 .../ide/idea/model/ProjectLibraryTest.groovy       |   7 +-
 .../plugins/ide/idea/model/ProjectTest.groovy      |   3 -
 .../internal/ModuleDependencyBuilderTest.groovy    |   7 +-
 .../plugins/ide/internal/GeneratorTaskTest.groovy  |   4 +-
 .../plugins/ide/internal/IdePluginTest.groovy      |   4 +-
 .../configurer/DeduplicationTargetTest.groovy      |  15 +-
 .../configurer/ModuleNameDeduperTest.groovy        |   3 -
 .../internal/configurer/ProjectDeduperTest.groovy  |  11 +-
 .../internal/tooling/GradleBuildBuilderTest.groovy |  46 ++
 .../tooling/GradleProjectBuilderTest.groovy        |  42 ++
 .../ide/internal/tooling/TasksFactoryTest.groovy   |  52 ++
 .../eclipse/DefaultEclipseProjectTest.groovy       |  27 +
 ...rtifactToFileBuildOutcomeTransformerTest.groovy |  86 ---
 .../internal/provider/TasksFactoryTest.groovy      |  52 --
 subprojects/integ-test/integ-test.gradle           |   7 +-
 ...kCommandLineConfigurationIntegrationSpec.groovy | 252 -------
 .../org/gradle/debug/GradleBuildRunner.groovy      |   2 -
 .../org/gradle/debug/GradleRunConfiguration.groovy |   2 -
 .../integtests/ApplicationIntegrationSpec.groovy   |   2 +-
 .../integtests/ApplicationIntegrationTest.groovy   | 146 ++++
 .../BuildAggregationIntegrationTest.groovy         |   5 +-
 .../BuildScriptClasspathIntegrationTest.java       |   1 -
 .../BuildScriptErrorIntegrationTest.java           | 108 ---
 .../BuildScriptExecutionIntegrationTest.groovy     |   1 -
 .../integtests/CacheProjectIntegrationTest.groovy  |  18 +-
 .../integtests/CustomPluginIntegrationTest.groovy  |   4 +-
 .../DistributionLocatorIntegrationTest.groovy      |   8 +-
 .../ExternalScriptErrorIntegrationTest.groovy      |  93 ---
 .../ExternalScriptExecutionIntegrationTest.groovy  |   2 -
 ...ementalGroovyProjectBuildIntegrationTest.groovy |   2 +-
 .../IncrementalTasksIntegrationTest.groovy         | 343 +++++++++
 .../IncrementalTestIntegrationTest.groovy          |  94 ---
 .../integtests/InitScriptErrorIntegrationTest.java |  51 --
 .../InitScriptExecutionIntegrationTest.groovy      |   1 -
 .../integtests/JavaProjectIntegrationTest.groovy   |  10 +-
 .../MixedNativeAndJvmProjectIntegrationTest.groovy |  39 ++
 .../MultiProjectDependencyIntegrationTest.groovy   |  12 +-
 .../OsgiProjectSampleIntegrationTest.groovy        |   5 +-
 .../ParallelProjectExecutionIntegrationTest.groovy |  39 +-
 .../PluginCrossVersionIntegrationTest.groovy       |  72 --
 .../integtests/ProfilingIntegrationTest.groovy     |  41 --
 .../integtests/ProjectLayoutIntegrationTest.groovy |  15 +-
 .../SettingsScriptErrorIntegrationTest.java        |  39 --
 .../SettingsScriptExecutionIntegrationTest.groovy  |   1 -
 .../TaskDefinitionIntegrationSpec.groovy           |  30 +
 .../TaskErrorExecutionIntegrationTest.groovy       |   8 +-
 .../integtests/TaskExecutionIntegrationTest.groovy | 341 +++++++++
 .../integtests/TaskExecutionIntegrationTest.java   | 180 -----
 ...ssingBinaryCompatibilityCrossVersionSpec.groovy |  81 +++
 .../integtests/WaterProjectIntegrationTest.groovy  |   3 -
 .../integtests/WebProjectIntegrationTest.java      |   2 +-
 .../BuildEnvironmentIntegrationTest.groovy         |   3 -
 .../fixture/TempDirIsUniquePerTestSpec.groovy      |   3 -
 .../logging/LoggingIntegrationTest.groovy          |  36 -
 .../ivy/IvyHttpPublishIntegrationTest.groovy       | 141 ++--
 .../ivy/IvyLocalPublishIntegrationTest.groovy      |   5 +-
 .../ivy/IvySFtpPublishIntegrationTest.groovy       |  11 +-
 .../IvyUrlResolverPublishIntegrationTest.groovy    |  75 ++
 .../ivy/SamplesIvyPublishIntegrationTest.groovy    |   3 -
 ...SamplesMavenPomGenerationIntegrationTest.groovy |   5 +-
 .../SamplesMavenQuickstartIntegrationTest.groovy   |   3 -
 .../AutoTestedSamplesCoreIntegrationTest.groovy    |   3 -
 .../AutoTestedSamplesPluginsIntegrationTest.groovy |   3 -
 .../samples/JUnitSamplesIntegrationTest.groovy     |  42 ++
 .../SamplesCodeQualityIntegrationTest.groovy       |   3 -
 ...lesExcludesAndClassifiersIntegrationTest.groovy |   3 -
 ...SamplesGroovyMultiProjectIntegrationTest.groovy |   3 -
 .../samples/SamplesJUnitIntegrationTest.groovy     |  44 ++
 .../samples/SamplesJavaBaseIntegrationTest.groovy  |   3 -
 ...mplesJavaCustomizedLayoutIntegrationTest.groovy |   3 -
 .../SamplesJavaMultiProjectIntegrationTest.groovy  |   3 -
 ...esJavaProjectWithIntTestsIntegrationTest.groovy |   3 -
 .../SamplesJavaQuickstartIntegrationTest.groovy    |   3 -
 .../SamplesRepositoriesIntegrationTest.groovy      |   3 -
 .../SamplesWebProjectIntegrationTest.groovy        |  11 +-
 .../SamplesWebQuickstartIntegrationTest.groovy     |   3 -
 .../canExecuteCommands/canExecuteCommands.gradle   |   4 +-
 .../canExecuteJava/canExecuteJava.gradle           |   4 +-
 .../LoggingIntegrationTest/deprecated/build.gradle |   7 -
 .../logging/project1/build.gradle                  |   8 +-
 .../internal-integ-testing.gradle                  |   4 +-
 .../fixtures/AbstractAutoTestedSamplesTest.groovy  |   5 +-
 .../fixtures/AbstractCompatibilityTestRunner.java  |  18 +-
 .../fixtures/AbstractIntegrationSpec.groovy        |  14 +-
 .../fixtures/AbstractMultiTestRunner.java          | 314 ++++++---
 .../fixtures/AutoTestedSamplesUtil.groovy          |   3 -
 .../integtests/fixtures/AvailableJavaHomes.java    |  41 +-
 .../fixtures/CrossVersionTestRunner.groovy         |   2 +-
 .../integtests/fixtures/IntegrationTestHint.java   |   3 -
 .../fixtures/KillProcessAvailability.groovy        |   3 -
 .../fixtures/TestNGExecutionResult.groovy          |  11 +-
 .../fixtures/UserGuideSamplesRunner.groovy         |  12 +-
 .../fixtures/executer/AnyOrderOutputMatcher.groovy |  51 ++
 .../executer/DefaultGradleDistribution.java        |  28 +-
 .../executer/DependencyResolutionFailure.groovy    |   3 -
 .../executer/DetailedExecutionFailure.groovy       |   5 +-
 .../executer/DownloadableGradleDistribution.groovy |   3 +-
 .../fixtures/executer/ExecutionResult.java         |   2 +-
 .../fixtures/executer/GradleDistribution.java      |   5 +
 .../fixtures/executer/InProcessGradleExecuter.java | 119 ++--
 .../executer/InitScriptExecuterFixture.groovy      |   7 +-
 .../executer/OutputScrapingExecutionFailure.java   |  80 ++-
 .../executer/OutputScrapingExecutionResult.java    |   7 +-
 .../executer/ParallelForkingGradleHandle.java      |   5 +-
 .../fixtures/executer/ParallelOutputMatcher.groovy |  51 --
 .../org/gradle/test/fixtures/AbstractModule.groovy |  77 ++
 .../test/fixtures/ivy/AbstractIvyModule.groovy     |  25 -
 .../gradle/test/fixtures/ivy/IvyDescriptor.groovy  |   2 +
 .../gradle/test/fixtures/ivy/IvyFileModule.groovy  |  71 +-
 .../test/fixtures/ivy/IvyFileRepository.groovy     |   8 +
 .../gradle/test/fixtures/ivy/IvyHttpModule.groovy  |  48 +-
 .../org/gradle/test/fixtures/ivy/IvyModule.java    |  11 +-
 .../gradle/test/fixtures/maven/HttpArtifact.groovy |   4 +
 .../test/fixtures/maven/MavenDependency.groovy     |   5 +
 .../test/fixtures/maven/MavenFileModule.groovy     | 122 ++--
 .../gradle/test/fixtures/maven/MavenModule.groovy  |   3 +-
 .../gradle/test/fixtures/maven/MavenScope.groovy   |   2 +-
 .../gradle/test/fixtures/publish/Identifier.java   | 101 ---
 .../test/fixtures/server/http/HttpServer.groovy    |  27 +-
 .../fixtures/server/http/ServletContainer.groovy   |  43 ++
 .../internal-testing/internal-testing.gradle       |   3 +-
 .../fixtures/DefaultTestExecutionResult.groovy     |   8 +
 .../fixtures/HtmlTestExecutionResult.groovy        |  17 +-
 .../fixtures/JUnitTestClassExecutionResult.groovy  | 160 +++++
 .../fixtures/JUnitXmlTestExecutionResult.groovy    | 123 +---
 .../fixtures/TestClassExecutionResult.java         |   4 +
 .../fixtures/TestResultOutputAssociation.java      |  22 +
 .../fixtures/archive/ArchiveTestFixture.groovy     |  81 +++
 .../test/fixtures/archive/JarTestFixture.groovy    |  31 +
 .../test/fixtures/archive/TarTestFixture.groovy    |  41 ++
 .../test/fixtures/archive/ZipTestFixture.groovy    |  37 +
 .../gradle/test/fixtures/encoding/Identifier.java  | 100 +++
 .../gradle/test/fixtures/file/ExecOutput.groovy    |  29 +
 .../org/gradle/test/fixtures/file/TestFile.java    |  12 +-
 .../test/fixtures/file/TestFileHelper.groovy       |  14 +-
 .../gradle/test/fixtures/file/WorkspaceTest.groovy |  34 +
 .../gradle/testing/internal/util/Network.groovy    |  29 -
 .../main/groovy/org/gradle/util/Assertions.groovy  |   3 -
 .../org/gradle/util/JUnit4GroovyMockery.java       |   3 -
 .../org/gradle/util/ReflectionEqualsMatcher.java   |   3 -
 .../src/main/groovy/org/gradle/util/Resources.java |   2 +-
 .../groovy/org/gradle/util/TestClassLoader.groovy  |  62 ++
 .../groovy/org/gradle/util/TestPrecondition.groovy |  18 +-
 .../groovy/org/gradle/util/AssertionsTest.groovy   |   3 -
 .../gradle/util/TempDirIsUniquePerTestSpec.groovy  |   3 -
 subprojects/ivy/ivy.gradle                         |   2 +-
 ...IvyPublishArtifactCustomisationIntegTest.groovy | 321 ---------
 ...IvyPublishArtifactCustomizationIntegTest.groovy | 321 +++++++++
 .../publish/ivy/IvyPublishBasicIntegTest.groovy    |   5 +-
 .../ivy/IvyPublishCoordinatesIntegTest.groovy      | 132 ++++
 .../IvyPublishCrossVersionIntegrationTest.groovy   |  30 +-
 ...yPublishDescriptorCustomisationIntegTest.groovy | 147 ----
 ...yPublishDescriptorCustomizationIntegTest.groovy | 149 ++++
 .../api/publish/ivy/IvyPublishHttpIntegTest.groovy | 145 ++--
 .../IvyPublishIdentifierValidationIntegTest.groovy |   4 +-
 .../api/publish/ivy/IvyPublishJavaIntegTest.groovy |   1 -
 .../ivy/IvyPublishMultiProjectIntegTest.groovy     |  56 ++
 .../ivy/SamplesIvyPublishIntegrationTest.groovy    |  42 +-
 .../org/gradle/api/publish/ivy/IvyArtifactSet.java |   2 +-
 .../api/publish/ivy/IvyConfigurationContainer.java |   2 +-
 .../org/gradle/api/publish/ivy/IvyDependency.java  |  15 +
 .../api/publish/ivy/IvyModuleDescriptor.java       |  10 +
 .../org/gradle/api/publish/ivy/IvyPublication.java |  52 +-
 .../ivy/internal/artifact/DefaultIvyArtifact.java  |   5 +
 .../artifact/IvyArtifactNotationParserFactory.java |  39 +-
 .../internal/dependency/DefaultIvyDependency.java  |  40 +-
 .../internal/dependency/IvyDependencyInternal.java |   4 +-
 ...tionDynamicDescriptorGenerationTaskCreator.java |   9 +-
 .../plugins/IvyPublishDynamicTaskCreator.java      |   5 +-
 .../publication/DefaultIvyModuleDescriptor.java    |   3 +-
 .../publication/DefaultIvyPublication.java         |  46 +-
 .../publication/IvyModuleDescriptorInternal.java   |   4 -
 .../publication/IvyPublicationInternal.java        |   3 +-
 .../publisher/ContextualizingIvyPublisher.java     |  40 ++
 .../publisher/DependencyResolverIvyPublisher.java  |  16 +-
 .../publisher/IvyDescriptorFileGenerator.java      |  22 +-
 .../internal/publisher/ValidatingIvyPublisher.java |  19 +-
 .../api/publish/ivy/plugins/IvyPublishPlugin.java  |  22 +-
 .../publish/ivy/tasks/PublishToIvyRepository.java  |  10 +-
 .../IvyArtifactNotationParserFactoryTest.groovy    |   9 +-
 ...namicDescriptorGenerationTaskCreatorTest.groovy |   8 +-
 .../IvyPublishDynamicTaskCreatorTest.groovy        |   4 +-
 .../publication/DefaultIvyPublicationTest.groovy   | 195 +++++-
 .../IvyDescriptorFileGeneratorTest.groovy          |  34 +-
 .../publisher/ValidatingIvyPublisherTest.groovy    |   3 +-
 .../ivy/plugins/IvyPublishPluginTest.groovy        |  14 +-
 .../ivy/tasks/PublishToIvyRepositoryTest.groovy    |   6 +-
 subprojects/jacoco/jacoco.gradle                   |  27 +
 .../plugins/JacocoPluginGoodBehaviourTest.groovy   |  33 +
 .../plugins/JacocoPluginIntegrationTest.groovy     | 199 ++++++
 .../jacoco/plugins/JacocoVersionIntegTest.groovy   |  69 ++
 .../gradle/internal/jacoco/JacocoAgentJar.groovy   |  57 ++
 .../jacoco/JacocoReportsContainerImpl.java         |  49 ++
 .../testing/jacoco/plugins/JacocoPlugin.groovy     | 188 +++++
 .../jacoco/plugins/JacocoPluginExtension.groovy    |  87 +++
 .../jacoco/plugins/JacocoTaskExtension.groovy      | 189 +++++
 .../gradle/testing/jacoco/tasks/JacocoBase.groovy  |  33 +
 .../gradle/testing/jacoco/tasks/JacocoMerge.groovy |  92 +++
 .../testing/jacoco/tasks/JacocoReport.groovy       | 258 +++++++
 .../jacoco/tasks/JacocoReportsContainer.java       |  47 ++
 .../gradle/testing/jacoco/tasks/package-info.java  |  20 +
 .../META-INF/gradle-plugins/jacoco.properties      |   1 +
 .../testing/jacoco/plugins/JacocoPluginSpec.groovy |  59 ++
 .../jacoco/plugins/JacocoTaskExtensionSpec.groovy  |  99 +++
 subprojects/javascript/javascript.gradle           |   2 +-
 .../coffeescript/CoffeeScriptBasePlugin.groovy     |   2 +-
 .../plugins/javascript/envjs/EnvJsPlugin.groovy    |   2 +-
 .../plugins/javascript/jshint/JsHintPlugin.groovy  |   2 +-
 subprojects/jetty/jetty.gradle                     |   2 +-
 .../api/plugins/jetty/AbstractJettyRunTask.java    |  41 +-
 .../org/gradle/api/plugins/jetty/JettyPlugin.java  |   8 +-
 .../api/plugins/jetty/JettyPluginConvention.java   |   2 -
 .../org/gradle/api/plugins/jetty/JettyRun.java     |   4 +-
 .../api/plugins/jetty/JettyPluginTest.groovy       |   8 +-
 subprojects/language-base/language-base.gradle     |   7 +
 .../groovy/org/gradle/language/base/Binary.java    |  28 +
 .../org/gradle/language/base/BinaryContainer.java  |  25 +
 .../language/base/BuildableModelElement.java       |  39 ++
 .../gradle/language/base/FunctionalSourceSet.java  |  27 +
 .../gradle/language/base/LanguageSourceSet.java    |  41 ++
 .../org/gradle/language/base/ProjectSourceSet.java |  26 +
 .../internal/AbstractBuildableModelElement.java    |  51 ++
 .../language/base/internal/BinaryInternal.java     |  23 +
 .../language/base/internal/BinaryNamingScheme.java |  31 +
 .../base/internal/DefaultBinaryContainer.java      |  27 +
 .../base/internal/DefaultBinaryNamingScheme.java   | 106 +++
 .../base/internal/DefaultFunctionalSourceSet.java  |  34 +
 .../base/internal/DefaultProjectSourceSet.java     |  32 +
 .../base/internal/LanguageSourceSetInternal.java   |  29 +
 .../org/gradle/language/base/package-info.java     |  23 +
 .../language/base/plugins/LanguageBasePlugin.java  |  59 ++
 .../gradle/language/base/plugins/package-info.java |  23 +
 .../base/internal/BuildableModelElementTest.groovy |  54 ++
 .../internal/DefaultBinaryNamingSchemeTest.groovy  |  69 ++
 subprojects/language-jvm/language-jvm.gradle       |   7 +
 .../org/gradle/language/java/JavaSourceSet.java    |  28 +
 .../java/internal/DefaultJavaSourceSet.java        |  78 +++
 .../org/gradle/language/java/package-info.java     |  23 +
 .../gradle/language/jvm/ClassDirectoryBinary.java  |  40 ++
 .../groovy/org/gradle/language/jvm/Classpath.java  |  28 +
 .../org/gradle/language/jvm/ResourceSet.java       |  25 +
 .../internal/ClassDirectoryBinaryNamingScheme.java |  63 ++
 .../jvm/internal/DefaultClassDirectoryBinary.java  |  79 +++
 .../language/jvm/internal/DefaultClasspath.java    |  39 ++
 .../language/jvm/internal/DefaultResourceSet.java  |  60 ++
 .../jvm/internal/SimpleStaleClassCleaner.java      |  38 +
 .../language/jvm/internal/StaleClassCleaner.java   |  43 ++
 .../org/gradle/language/jvm/package-info.java      |  23 +
 .../language/jvm/plugins/JvmLanguagePlugin.java    | 107 +++
 .../gradle/language/jvm/plugins/package-info.java  |  23 +
 .../language/jvm/tasks/ProcessResources.java       |  35 +
 .../gradle/language/jvm/tasks/package-info.java    |  23 +
 .../java/internal/DefaultJavaSourceSetTest.groovy  |  33 +
 .../ClassDirectoryBinaryNamingSchemeTest.groovy    |  62 ++
 .../DefaultClassDirectoryBinaryTest.groovy         |  78 +++
 .../jvm/internal/DefaultResourceSetTest.groovy     |  32 +
 subprojects/launcher/launcher.gradle               |  12 +-
 .../ConfigurationOnDemandIntegrationTest.groovy    |   3 -
 ...EnablingParallelExecutionIntegrationTest.groovy |   3 -
 .../GradleConfigurabilityIntegrationSpec.groovy    |   4 -
 .../daemon/DaemonFeedbackIntegrationSpec.groovy    |   3 -
 ...itialCommunicationFailureIntegrationSpec.groovy |   3 -
 .../launcher/daemon/DaemonIntegrationSpec.groovy   |   3 -
 .../launcher/daemon/DaemonLifecycleSpec.groovy     |   2 +-
 .../daemon/DispachingFailureIntegrationSpec.groovy |   3 -
 .../launcher/daemon/EmbeddedDaemonSmokeTest.groovy |   4 +-
 .../gradle/launcher/daemon/ExecuteBuildAction.java |  28 +
 .../daemon/SingleUseDaemonIntegrationTest.groovy   |   3 +-
 .../daemon/StoppingDaemonIntegrationSpec.groovy    |   3 -
 .../daemon/testing/DaemonContextParser.java        |   3 -
 .../daemon/testing/DaemonLogsAnalyzer.groovy       |   3 -
 .../main/java/org/gradle/launcher/GradleMain.java  |   3 -
 .../src/main/java/org/gradle/launcher/Main.java    |   4 +-
 .../launcher/bootstrap/ProcessBootstrap.java       |   6 +-
 .../gradle/launcher/cli/BuildActionsFactory.java   |  93 ++-
 .../gradle/launcher/cli/ExecuteBuildAction.java    |  20 +-
 .../org/gradle/launcher/cli/RunBuildAction.java    |   6 +-
 .../cli/converter/DaemonCommandLineConverter.java  |  50 ++
 .../cli/converter/LayoutToPropertiesConverter.java |  73 ++
 .../PropertiesToDaemonParametersConverter.java     |  68 ++
 .../PropertiesToStartParameterConverter.java       |  36 +
 .../launcher/daemon/DaemonExecHandleBuilder.java   |   3 -
 .../launcher/daemon/bootstrap/DaemonGreeter.java   |   3 -
 .../daemon/bootstrap/DaemonOutputConsumer.java     |   3 -
 .../bootstrap/DaemonStartupCommunication.java      |   3 -
 .../launcher/daemon/client/DaemonClient.java       |  30 +-
 .../daemon/client/DaemonClientConnection.java      |  42 +-
 .../daemon/client/DaemonClientServicesSupport.java |  11 +-
 .../daemon/client/DaemonConnectionException.java   |  30 +
 .../launcher/daemon/client/DaemonConnector.java    |   9 +-
 .../client/DaemonInitialConnectException.java      |   4 +
 .../daemon/client/DefaultDaemonConnector.java      |  61 +-
 .../client/EmbeddedDaemonClientServices.java       |  10 +-
 .../client/NoUsableDaemonFoundException.java       |   3 -
 .../daemon/client/SingleUseDaemonClient.java       |  17 +-
 .../daemon/client/StaleDaemonAddressException.java |  29 +
 .../daemon/client/StopDaemonClientServices.java    |   2 +-
 .../launcher/daemon/client/StopDispatcher.java     |   3 -
 .../daemon/configuration/DaemonParameters.java     |  40 +-
 .../configuration/DaemonServerConfiguration.java   |   3 -
 .../DefaultDaemonServerConfiguration.java          |   3 -
 .../ForegroundDaemonConfiguration.java             |   3 -
 .../daemon/configuration/GradleProperties.java     | 177 +----
 .../configuration/GradlePropertiesConfigurer.java  |  48 --
 .../daemon/diagnostics/DaemonDiagnostics.java      |   2 -
 .../daemon/diagnostics/DaemonStartupInfo.java      |   3 -
 .../org/gradle/launcher/daemon/protocol/Build.java |   8 +-
 .../launcher/daemon/protocol/BuildAndStop.java     |   4 +-
 .../daemon/registry/DaemonRegistryContent.java     |   3 -
 .../daemon/registry/DaemonRegistryServices.java    |   5 +-
 .../daemon/registry/PersistentDaemonRegistry.java  |  30 +-
 .../launcher/daemon/server/DaemonServices.java     |   8 +-
 .../daemon/server/DomainRegistryUpdater.java       |   3 -
 .../server/SynchronizedDispatchConnection.java     |   2 -
 .../launcher/daemon/server/exec/ExecuteBuild.java  |   4 +-
 .../daemon/server/exec/ForwardClientInput.java     |   4 +-
 .../gradle/launcher/exec/BuildActionExecuter.java  |  29 +
 .../exec/GradleLauncherActionExecuter.java         |  29 -
 .../exec/InProcessBuildActionExecuter.java         |  77 ++
 .../InProcessGradleLauncherActionExecuter.java     |  46 --
 .../gradle/launcher/exec/InitializationAware.java  |  22 -
 .../internal/provider/BuildActionResult.java       |  33 +
 .../internal/provider/BuildModelAction.java        |  99 +++
 .../internal/provider/ClassLoaderDetails.java      |  36 +
 .../internal/provider/ClasspathInferer.java        | 131 ++++
 .../provider/ClientProvidedBuildAction.java        |  81 +++
 .../ClientSidePayloadClassLoaderRegistry.java      | 142 ++++
 .../internal/provider/ConfiguringBuildAction.java  |  47 +-
 .../internal/provider/ConnectionScopeServices.java |  47 ++
 .../provider/DaemonBuildActionExecuter.java        |  47 ++
 .../DaemonGradleLauncherActionExecuter.java        |  47 --
 .../internal/provider/DefaultBuildController.java  |  61 ++
 .../internal/provider/DefaultConnection.java       | 200 +++---
 .../provider/DefaultConnectionMetaData.java        |  30 +
 .../DefaultPayloadClassLoaderRegistry.java         | 174 +++++
 .../provider/DelegatingBuildModelAction.java       |  58 --
 .../tooling/internal/provider/DeserializeMap.java  |  24 +
 .../internal/provider/EmbeddedExecuterSupport.java |  45 --
 .../internal/provider/ExecuteBuildAction.java      |  33 -
 .../LoggingBridgingBuildActionExecuter.java        |  75 ++
 ...oggingBridgingGradleLauncherActionExecuter.java |  75 --
 .../internal/provider/ModelClassLoaderFactory.java | 126 ++++
 .../provider/PayloadClassLoaderRegistry.java       |  26 +
 .../internal/provider/PayloadSerializer.java       | 155 +++++
 .../internal/provider/ProviderConnection.java      | 165 +++++
 .../internal/provider/ReflectionClassLookup.java   |  44 ++
 .../tooling/internal/provider/SerializeMap.java    |  36 +
 .../internal/provider/SerializedPayload.java       |  37 +
 .../provider/ToolingGlobalScopeServices.java       |  27 +
 .../tooling/internal/provider/ToolingServices.java |  27 +
 .../connection/AdaptedOperationParameters.java     |   5 +-
 .../connection/ProviderOperationParameters.java    |   2 -
 .../internal/provider/jdk6/Jdk6ClassLookup.java    |  27 +
 ...e.internal.service.scopes.PluginServiceRegistry |   1 +
 .../launcher/cli/BuildActionsFactoryTest.groovy    |  53 +-
 .../gradle/launcher/cli/RunBuildActionTest.groovy  |   4 +-
 .../DaemonCommandLineConverterTest.groovy          |  39 ++
 .../LayoutToPropertiesConverterTest.groovy         |  95 +++
 ...ropertiesToDaemonParametersConverterTest.groovy | 106 +++
 .../PropertiesToStartParameterConverterTest.groovy |  36 +
 .../daemon/DaemonExecHandleBuilderSpec.groovy      |   3 -
 .../daemon/bootstrap/DaemonGreeterTest.groovy      |   3 -
 .../bootstrap/DaemonOutputConsumerTest.groovy      |   3 -
 .../DaemonStartupCommunicationSpec.groovy          |   3 -
 .../client/DaemonClientConnectionTest.groovy       |  78 ++-
 .../daemon/client/DaemonClientServicesTest.groovy  |   3 +-
 .../launcher/daemon/client/DaemonClientTest.groovy |  18 +-
 .../client/DefaultDaemonConnectorTest.groovy       |  37 +-
 .../daemon/client/StopDispatcherTest.groovy        |   5 +-
 .../daemon/configuration/CurrentProcessTest.groovy |   3 +-
 .../configuration/DaemonParametersTest.groovy      |  82 +--
 .../GradlePropertiesConfigurerTest.groovy          |  77 --
 .../configuration/GradlePropertiesTest.groovy      | 218 ------
 .../diagnostics/DaemonDiagnosticsTest.groovy       |   3 -
 .../registry/DomainRegistryUpdaterTest.groovy      |   3 -
 .../registry/PersistentDaemonRegistryTest.groovy   |  34 +-
 .../DaemonServerExceptionHandlingTest.groovy       |  17 +-
 .../server/DaemonStateCoordinatorTest.groovy       |   3 -
 .../exec/DefaultBuildActionParametersTest.groovy   |   8 +-
 .../exec/InProcessBuildActionExecuterTest.groovy   | 156 +++++
 ...nProcessGradleLauncherActionExecuterTest.groovy | 100 ---
 .../provider/AbstractClassGraphSpec.groovy         |  63 ++
 .../internal/provider/ClasspathInfererTest.groovy  |  66 ++
 .../provider/ConfiguringBuildActionTest.groovy     |  36 +-
 .../provider/ConnectionScopeServicesTest.groovy    |  34 +
 .../tooling/internal/provider/CustomAction.java    |  34 +
 .../tooling/internal/provider/CustomModel.java     |  24 +
 .../tooling/internal/provider/CustomPayload.java   |  29 +
 .../provider/DaemonBuildActionExecuterTest.groovy  |  45 ++
 .../DaemonGradleLauncherActionExecuterTest.groovy  |  45 --
 .../provider/DefaultBuildControllerTest.groovy     |  92 +++
 .../provider/ExecuteBuildActionTest.groovy         |  37 -
 .../LoggingBridgingBuildActionExecuterTest.groovy  |  74 ++
 ...BridgingGradleLauncherActionExecuterTest.groovy |  74 --
 .../provider/ModelClassLoaderFactoryTest.groovy    |  36 +
 .../internal/provider/PayloadInterface.java        |  21 +
 .../internal/provider/PayloadSerializerTest.groovy | 204 ++++++
 .../provider/ToolingGlobalScopeServicesTest.groovy |  28 +
 .../tooling/internal/provider/WrapperPayload.java  |  27 +
 .../AdaptedOperationParametersTest.groovy          |   3 -
 .../connection/BuildLogLevelMixInTest.groovy       |   3 -
 subprojects/maven/maven.gradle                     |   2 +-
 .../maven/MavenConversionIntegrationTest.groovy    | 161 -----
 .../maven/AbstractMavenPublishIntegTest.groovy     |  11 +-
 ...venPublishArtifactCustomisationIntegTest.groovy | 228 ------
 ...venPublishArtifactCustomizationIntegTest.groovy | 255 +++++++
 .../maven/MavenPublishBasicIntegTest.groovy        |   8 +-
 .../maven/MavenPublishCoordinatesIntegTest.groovy  | 140 ++++
 .../MavenPublishCrossVersionIntegrationTest.groovy |  21 +-
 ...avenPublishIdentifierValidationIntegTest.groovy |   4 +-
 .../maven/MavenPublishIssuesIntegTest.groovy       |  51 ++
 .../publish/maven/MavenPublishJavaIntegTest.groovy |  30 +-
 .../maven/MavenPublishMultiProjectIntegTest.groovy |  88 ++-
 .../MavenPublishPomCustomisationIntegTest.groovy   | 174 -----
 .../MavenPublishPomCustomizationIntegTest.groovy   | 206 ++++++
 .../SamplesMavenPublishIntegrationTest.groovy      |  42 +-
 .../api/artifacts/maven/Conf2ScopeMapping.java     |   2 -
 .../maven/Conf2ScopeMappingContainer.java          |  10 +-
 .../api/artifacts/maven/GroovyMavenDeployer.java   |   2 -
 .../gradle/api/artifacts/maven/MavenDeployer.java  |   6 +-
 .../api/artifacts/maven/MavenDeployment.java       |   2 +-
 .../org/gradle/api/artifacts/maven/MavenPom.java   |  34 +-
 .../gradle/api/artifacts/maven/MavenResolver.java  |   4 +-
 .../api/artifacts/maven/PomFilterContainer.java    |  40 +-
 .../gradle/api/artifacts/maven/PublishFilter.java  |   2 -
 .../groovy/org/gradle/api/plugins/MavenPlugin.java |   6 +-
 .../gradle/api/plugins/MavenPluginConvention.java  |   2 -
 .../plugins/MavenRepositoryHandlerConvention.java  |   2 +-
 .../api/plugins/maven/ConvertMaven2Gradle.groovy   |  54 --
 .../api/plugins/maven/Maven2GradlePlugin.groovy    |  36 -
 .../api/plugins/maven/internal/Maven2Gradle.groovy | 552 ---------------
 .../maven/internal/MavenProjectXmlWriter.java      |  61 --
 .../maven/internal/MavenProjectsCreator.java       |  98 ---
 .../publication/maven/internal/ArtifactPom.java    |   3 -
 .../maven/internal/ArtifactPomContainer.java       |   3 -
 .../maven/internal/ArtifactPomFactory.java         |   3 -
 .../maven/internal/BasePomFilterContainer.java     |   3 -
 .../maven/internal/DefaultArtifactPom.java         |   3 -
 .../internal/DefaultArtifactPomContainer.java      |   3 -
 .../maven/internal/DefaultArtifactPomFactory.java  |   3 -
 .../DefaultConf2ScopeMappingContainer.java         |   5 +-
 .../maven/internal/DefaultMavenDeployment.java     |   3 -
 .../maven/internal/DefaultMavenPom.java            |   5 +-
 .../maven/internal/DefaultMavenPomFactory.java     |   5 +-
 .../maven/internal/DefaultPomFilter.java           |   3 -
 .../maven/internal/ExcludeRuleConverter.java       |   3 -
 .../maven/internal/PomDependenciesConverter.java   |   3 -
 .../api/publication/maven/internal/PomFilter.java  |   3 -
 .../maven/internal/ant/AbstractMavenResolver.java  |  13 +-
 .../maven/internal/ant/BaseMavenDeployer.java      |   5 +-
 .../maven/internal/ant/BaseMavenInstaller.java     |   5 +-
 .../maven/internal/ant/CustomDeployTask.java       |   2 -
 .../ant/CustomInstallDeployTaskSupport.java        |   5 +-
 .../maven/internal/ant/CustomInstallTask.java      |   3 -
 .../internal/ant/DefaultDeployTaskFactory.java     |   3 -
 .../internal/ant/DefaultExcludeRuleConverter.java  |   3 -
 .../internal/ant/DefaultGroovyMavenDeployer.groovy |   4 -
 .../internal/ant/DefaultInstallTaskFactory.java    |   3 -
 .../ant/DefaultPomDependenciesConverter.java       |   3 -
 .../internal/ant/EmptyMavenSettingsSupplier.java   |   3 -
 .../maven/internal/ant/LoggingHelper.java          |   3 -
 .../maven/internal/ant/MavenSettingsSupplier.java  |   3 -
 .../ant/MaybeUserMavenSettingsSupplier.java        |   3 -
 .../maven/internal/ant/RepositoryBuilder.java      |   3 -
 .../maven/internal/ant/RepositoryFactory.java      |   3 -
 .../gradle/api/publish/maven/MavenArtifactSet.java |   2 +-
 .../gradle/api/publish/maven/MavenDependency.java  |  41 ++
 .../org/gradle/api/publish/maven/MavenPom.java     |  13 +-
 .../gradle/api/publish/maven/MavenPublication.java |  38 +-
 .../internal/artifact/DefaultMavenArtifact.java    |   5 +
 .../dependencies/DefaultMavenDependency.java       |  56 ++
 .../dependencies/MavenDependencyInternal.java      |  25 +
 .../internal/plugins/GeneratePomTaskCreator.java   |   4 +-
 .../plugins/MavenPublishDynamicTaskCreator.java    |   5 +-
 .../MavenPublishLocalDynamicTaskCreator.java       |   7 +-
 .../internal/publication/DefaultMavenPom.java      |  14 +-
 .../publication/DefaultMavenPublication.java       |  63 +-
 .../internal/publication/MavenPomInternal.java     |   6 +-
 .../publication/MavenPublicationInternal.java      |   7 +-
 .../publisher/AntTaskBackedMavenPublisher.java     |   2 +-
 .../publisher/MavenDeployerConfigurer.java         |  55 --
 .../publisher/MavenRemoteRepositoryFactory.java    |  50 ++
 .../publisher/ValidatingMavenPublisher.java        |  22 +-
 .../internal/tasks/MavenPomFileGenerator.java      |  87 +--
 .../publish/maven/plugins/MavenPublishPlugin.java  |  12 +-
 .../api/publish/maven/tasks/GenerateMavenPom.java  |  37 +-
 .../gradle-plugins/maven2Gradle.properties         |   1 -
 .../api/artifacts/maven/Conf2ScopeMappingTest.java |   3 -
 .../api/plugins/MavenPluginConventionTest.groovy   |  14 +-
 .../org/gradle/api/plugins/MavenPluginTest.java    |   9 +-
 .../plugins/maven/Maven2GradlePluginSpec.groovy    |  36 -
 .../internal/MavenProjectXmlWriterTest.groovy      |  34 -
 .../maven/internal/MavenProjectsCreatorSpec.groovy | 118 ----
 .../maven/internal/BasePomFilterContainerTest.java |   3 -
 .../maven/internal/DefaultArtifactPomTest.java     |   3 -
 .../DefaultConf2ScopeMappingContainerTest.java     |   3 -
 .../internal/DefaultMavenPomFactoryTest.groovy     |   7 +-
 .../maven/internal/DefaultPomFilterTest.java       |   3 -
 .../internal/ant/AbstractMavenResolverTest.java    |   5 +-
 .../maven/internal/ant/BaseMavenDeployerTest.java  |   5 +-
 .../maven/internal/ant/BaseMavenInstallerTest.java |   5 +-
 .../internal/ant/DefaultDeployTaskFactoryTest.java |   7 +-
 .../ant/DefaultExcludeRuleConverterTest.java       |   3 -
 .../ant/DefaultGroovyMavenDeployerTest.groovy      |   5 +-
 .../ant/DefaultGroovyPomFilterContainerTest.groovy |   7 +-
 .../ant/DefaultPomDependenciesConverterTest.java   |   3 -
 .../ant/EmptyMavenSettingsSupplierTest.groovy      |   3 -
 .../ant/MaybeUserMavenSettingsSupplierTest.groovy  |   4 -
 ...ectDependencyArtifactIdExtractorHackTest.groovy |   5 +-
 .../MavenArtifactNotationParserFactoryTest.groovy  |   4 +-
 .../publication/DefaultMavenPublicationTest.groovy | 179 ++++-
 .../publisher/ValidatingMavenPublisherTest.groovy  |  41 +-
 .../tasks/MavenPomFileGeneratorTest.groovy         |  70 +-
 .../maven/plugins/MavenPublishPluginTest.groovy    |  18 +-
 .../tasks/PublishToMavenRepositoryTest.groovy      |   6 +-
 subprojects/messaging/messaging.gradle             |   2 +-
 .../messaging/serialize/AbstractDecoder.java       |  60 ++
 .../messaging/serialize/AbstractEncoder.java       |  56 ++
 .../serialize/DataStreamBackedSerializer.java      |  36 -
 .../org/gradle/messaging/serialize/Decoder.java    | 106 +++
 .../messaging/serialize/DefaultSerializer.java     |  12 +-
 .../org/gradle/messaging/serialize/Encoder.java    |  85 +++
 .../messaging/serialize/FlushableEncoder.java      |  24 +
 .../serialize/InputStreamBackedDecoder.java        |  79 +++
 .../gradle/messaging/serialize/LongSerializer.java |  29 +
 .../serialize/OutputStreamBackedEncoder.java       |  85 +++
 .../org/gradle/messaging/serialize/Serializer.java |   7 +-
 .../serialize/kryo/KryoBackedDecoder.java          | 121 ++++
 .../serialize/kryo/KryoBackedEncoder.java          |  77 ++
 .../messaging/serialize/AbstractCodecTest.groovy   | 387 +++++++++++
 .../serialize/DefaultSerializerTest.groovy         |   8 +-
 .../messaging/serialize/LongSerializerTest.groovy  |  34 +
 .../serialize/StreamBackedCodecTest.groovy         |  31 +
 .../serialize/kryo/KryoBackedCodecTest.groovy      |  36 +
 .../messaging/serialize/SerializerSpec.groovy      |  30 +
 subprojects/native/native.gradle                   |   3 +-
 .../nativeplatform/ReflectiveEnvironment.java      |   2 -
 .../filesystem/FileSystemServices.java             |   5 +-
 .../nativeplatform/jna/JnaBootPathConfigurer.java  |   3 -
 subprojects/open-api/open-api.gradle               |   2 +-
 ...CrossVersionCompatibilityIntegrationTest.groovy |   4 +-
 .../integtests/openapi/OutputUILordTest.groovy     |   2 -
 .../TestAlternateUIInteractionVersion1.java        |   2 -
 .../openapi/TestSettingsNodeVersion1.java          |   2 -
 .../TestSingleDualPaneUIInteractionVersion1.java   |   1 -
 .../org/gradle/foundation/BootstrapLoader.java     | 189 -----
 .../gradle/foundation/ParentLastClassLoader.java   |  73 --
 .../gradle/openapi/external/ExternalUtility.java   | 168 -----
 .../foundation/GradleInterfaceVersion1.java        |  89 ---
 .../foundation/GradleInterfaceVersion2.java        |  89 ---
 .../external/foundation/ProjectVersion1.java       |  83 ---
 .../foundation/RequestObserverVersion1.java        |  52 --
 .../external/foundation/RequestVersion1.java       |  62 --
 .../openapi/external/foundation/TaskVersion1.java  |  61 --
 .../foundation/favorites/FavoriteTaskVersion1.java |  48 --
 .../favorites/FavoritesEditorVersion1.java         | 105 ---
 .../external/runner/GradleRunnerFactory.java       | 134 ----
 .../runner/GradleRunnerInteractionVersion1.java    |  83 ---
 .../external/runner/GradleRunnerVersion1.java      |  35 -
 .../ui/AlternateUIInteractionVersion1.java         |  59 --
 .../openapi/external/ui/BasicGradleUIVersion1.java | 199 ------
 ...ommandLineArgumentAlteringListenerVersion1.java |  32 -
 .../external/ui/DualPaneUIInteractionVersion1.java |  29 -
 .../openapi/external/ui/DualPaneUIVersion1.java    |  56 --
 .../openapi/external/ui/GradleTabVersion1.java     |  51 --
 .../external/ui/GradleUIInteractionVersion1.java   |  42 --
 .../external/ui/OutputObserverVersion1.java        |  56 --
 .../openapi/external/ui/OutputUILordVersion1.java  |  70 --
 .../openapi/external/ui/SettingsNodeVersion1.java  |  67 --
 .../ui/SinglePaneUIInteractionVersion1.java        |  29 -
 .../openapi/external/ui/SinglePaneUIVersion1.java  |  39 --
 .../org/gradle/openapi/external/ui/UIFactory.java  | 239 -------
 .../org/gradle/foundation/BootstrapLoader.java     | 187 +++++
 .../gradle/foundation/ParentLastClassLoader.java   |  72 ++
 .../gradle/openapi/external/ExternalUtility.java   | 166 +++++
 .../foundation/GradleInterfaceVersion1.java        |  89 +++
 .../foundation/GradleInterfaceVersion2.java        |  88 +++
 .../external/foundation/ProjectVersion1.java       |  83 +++
 .../foundation/RequestObserverVersion1.java        |  52 ++
 .../external/foundation/RequestVersion1.java       |  62 ++
 .../openapi/external/foundation/TaskVersion1.java  |  61 ++
 .../foundation/favorites/FavoriteTaskVersion1.java |  45 ++
 .../favorites/FavoritesEditorVersion1.java         | 105 +++
 .../external/runner/GradleRunnerFactory.java       | 133 ++++
 .../runner/GradleRunnerInteractionVersion1.java    |  85 +++
 .../external/runner/GradleRunnerVersion1.java      |  35 +
 .../ui/AlternateUIInteractionVersion1.java         |  59 ++
 .../openapi/external/ui/BasicGradleUIVersion1.java | 199 ++++++
 ...ommandLineArgumentAlteringListenerVersion1.java |  32 +
 .../external/ui/DualPaneUIInteractionVersion1.java |  30 +
 .../openapi/external/ui/DualPaneUIVersion1.java    |  59 ++
 .../openapi/external/ui/GradleTabVersion1.java     |  48 ++
 .../external/ui/GradleUIInteractionVersion1.java   |  41 ++
 .../external/ui/OutputObserverVersion1.java        |  59 ++
 .../openapi/external/ui/OutputUILordVersion1.java  |  70 ++
 .../openapi/external/ui/SettingsNodeVersion1.java  |  67 ++
 .../ui/SinglePaneUIInteractionVersion1.java        |  30 +
 .../openapi/external/ui/SinglePaneUIVersion1.java  |  40 ++
 .../org/gradle/openapi/external/ui/UIFactory.java  | 238 +++++++
 subprojects/osgi/osgi.gradle                       |   4 +-
 .../plugins/osgi/ContainedVersionAnalyzer.java     |   2 +-
 .../plugins/osgi/DefaultAnalyzerFactory.java       |   3 -
 .../internal/plugins/osgi/DefaultOsgiManifest.java |   5 +-
 .../api/internal/plugins/osgi/OsgiHelper.java      |   3 -
 .../org/gradle/api/plugins/osgi/OsgiManifest.java  |   4 +-
 .../org/gradle/api/plugins/osgi/OsgiPlugin.groovy  |   2 -
 .../api/plugins/osgi/OsgiPluginConvention.java     |   2 -
 .../plugins/osgi/DefaultAnalyzerFactoryTest.java   |   4 +-
 .../plugins/osgi/DefaultOsgiManifestTest.groovy    |   8 +-
 .../plugins/osgi/OsgiPluginConventionTest.groovy   |  12 +-
 .../gradle/api/plugins/osgi/OsgiPluginTest.groovy  |   4 +-
 subprojects/performance/performance.gradle         | 107 ++-
 .../performance/CleanBuildPerformanceTest.groovy   |   8 +-
 .../DependencyReportPerformanceTest.groovy         |  12 +-
 .../DependencyResolutionStressTest.groovy          |   5 +-
 .../performance/FirstBuildPerformanceTest.groovy   |  47 ++
 .../IdeIntegrationPerformanceTest.groovy           |  14 +-
 .../TestExecutionPerformanceTest.groovy            |  10 +-
 .../UpToDateBuildPerformanceTest.groovy            |   8 +-
 .../src/templates/project-with-source/build.gradle |   1 +
 .../gradle/performance/ResultSpecification.groovy  |  48 ++
 .../gradle/performance/fixture/AmountTest.groovy   | 231 ------
 .../gradle/performance/fixture/DurationTest.groovy |  59 --
 .../fixture/PerformanceResultsTest.groovy          | 181 ++---
 .../fixture/PerformanceTestRunnerTest.groovy       | 105 +++
 .../fixture/PrettyCalculatorSpec.groovy            |   5 +-
 .../gradle/performance/fixture/UnitsTest.groovy    |  34 -
 .../gradle/performance/measure/AmountTest.groovy   | 231 ++++++
 .../gradle/performance/measure/DurationTest.groovy |  59 ++
 .../gradle/performance/measure/UnitsTest.groovy    |  34 +
 .../performance/results/ReportGeneratorTest.groovy |  44 ++
 .../performance/results/ResultsStoreTest.groovy    | 181 +++++
 .../fixture/AbstractPerformanceTest.groovy         |  30 +-
 .../org/gradle/performance/fixture/Amount.java     | 162 -----
 .../performance/fixture/BaselineVersion.groovy     |  20 +-
 .../performance/fixture/CompositeDataReporter.java |  39 ++
 .../org/gradle/performance/fixture/DataAmount.java |  37 -
 .../gradle/performance/fixture/DataCollector.java  |   5 +-
 .../org/gradle/performance/fixture/Duration.java   |  45 --
 .../org/gradle/performance/fixture/Git.groovy      |  42 ++
 .../performance/fixture/MeasuredOperation.groovy   |  41 --
 .../fixture/MeasuredOperationList.groovy           |   8 +-
 .../performance/fixture/MemoryInfoCollector.groovy |   6 +-
 .../gradle/performance/fixture/OperationTimer.java |  36 +
 .../performance/fixture/PerformanceResults.groovy  |  51 +-
 .../fixture/PerformanceTestRunner.groovy           |  65 +-
 .../performance/fixture/PrettyCalculator.groovy    |   7 +-
 .../performance/fixture/TestProjectLocator.groovy  |   3 -
 .../fixture/TextFileDataReporter.groovy            |  13 +-
 .../org/gradle/performance/fixture/Units.java      | 193 ------
 .../org/gradle/performance/measure/Amount.java     | 163 +++++
 .../org/gradle/performance/measure/DataAmount.java |  41 ++
 .../org/gradle/performance/measure/Duration.java   |  45 ++
 .../performance/measure/MeasuredOperation.groovy   |  22 +
 .../org/gradle/performance/measure/Units.java      | 193 ++++++
 .../gradle/performance/results/FileRenderer.java   |  34 +
 .../gradle/performance/results/FormatSupport.java  |  56 ++
 .../performance/results/HtmlPageGenerator.java     |  51 ++
 .../performance/results/IndexPageGenerator.java    | 106 +++
 .../performance/results/ReportGenerator.java       |  53 ++
 .../gradle/performance/results/ResultsStore.java   | 239 +++++++
 .../performance/results/TestDataGenerator.java     | 102 +++
 .../performance/results/TestExecutionHistory.java  |  66 ++
 .../performance/results/TestPageGenerator.java     | 143 ++++
 .../resources/org/gradle/reporting/style.css       |  59 ++
 subprojects/plugins/plugins.gradle                 |   5 +-
 .../gradle/api/plugins/BuildSrcPluginTest.groovy   |   2 +-
 .../api/tasks/bundling/JarIntegrationTest.groovy   | 155 ++++-
 .../tasks/bundling/WarTaskIntegrationTest.groovy   | 102 +++
 .../groovy/GroovyBasePluginIntegrationTest.groovy  |  80 ++-
 .../groovy/GroovyPluginIntegrationTest.groovy      |  52 ++
 .../AntForkingGroovyCompilerIntegrationTest.groovy |   2 +-
 .../BasicGroovyCompilerIntegrationSpec.groovy      |   2 +-
 .../compile/GroovyCompilerIntegrationSpec.groovy   |  12 +
 .../JreJavaHomeGroovyIntegrationTest.groovy        |  29 +-
 .../gradle/java/JavaPluginGoodBehaviourTest.groovy |  22 +
 .../JreJavaHomeJavaIntegrationTest.groovy          |  30 +-
 .../testing/IncrementalTestIntegrationTest.groovy  | 123 ++++
 .../testing/TestReportIntegrationTest.groovy       | 251 ++++++-
 .../gradle/testing/TestTaskIntegrationTest.groovy  |  51 ++
 .../gradle/testing/TestingIntegrationTest.groovy   |  34 +
 .../junit/JUnitAssumptionsIntegrationTest.groovy   |  60 ++
 ...nitCategoriesCrossVersionIntegrationSpec.groovy |  96 +++
 .../junit/JUnitCategoriesIntegrationSpec.groovy    |  82 +++
 .../junit/JUnitCrossVersionIntegrationSpec.groovy  |  25 +-
 ...itIgnoreClassCrossVersionIntegrationSpec.groovy |  58 ++
 .../testing/junit/JUnitIntegrationTest.groovy      |  21 +-
 .../testng/SampleTestNGIntegrationTest.groovy      |   3 -
 .../testing/testng/TestNGIntegrationProject.groovy |  67 --
 .../testing/testng/TestNGIntegrationTest.groovy    |   3 -
 .../testng/TestNGLoggingIntegrationTest.groovy     | 107 ++-
 .../TestNGProducesOldReportsIntegrationTest.groovy |   4 +-
 ...tNGXmlResultAndHtmlReportIntegrationTest.groovy | 299 +++++++-
 .../shared/build.gradle                            |   2 +-
 .../build.gradle                                   |  10 +
 .../src/main/groovy/GroovyCode.groovy              |   1 +
 .../src/main/groovy/JavaCode.java                  |   3 +
 .../build.gradle                                   |   2 +-
 .../recompilesDependentClasses/build.gradle        |   2 +-
 .../build.gradle                                   |   2 +-
 .../doesNotRunStaleTests/src/test/java/Broken.java |   0
 .../NewMainClass.java                              |   0
 .../executesTestsWhenSourceChanges/NewOk.java      |   0
 .../src/main/java/MainClass.java                   |   0
 .../build.gradle                                   |   0
 .../src/test/java/JUnitExtra.java                  |   0
 .../src/test/java/JUnitTest.java                   |   0
 .../src/test/java/TestNGTest.java                  |   0
 .../shared/build.gradle                            |   0
 .../shared/src/test/java/Ok.java                   |   0
 .../supportsAssumptions/build.gradle               |  18 +
 .../test/java/org/gradle/TestWithAssumptions.java  |  32 +
 .../build.gradle                                   |  28 +
 .../src/test/java/org/gradle/CategoryA.java        |  20 +
 .../src/test/java/org/gradle/LocaleHolder.java     |  32 +
 .../src/test/java/org/gradle/Locales.java          |  80 +++
 .../src/test/java/org/gradle/SomeLocaleTests.java  |  19 +
 .../test/java/org/gradle/SomeMoreLocalTests.java   |  35 +
 .../canSpecifyExcludesOnly/build.gradle            |  26 +
 .../src/test/java/org/gradle/CatATests.java        |  41 ++
 .../src/test/java/org/gradle/CategoryA.java        |  20 +
 .../src/test/java/org/gradle/NoCatTests.java       |  30 +
 .../src/test/java/org/gradle/SomeOtherCat.java     |  20 +
 .../test/java/org/gradle/SomeOtherCatTests.java    |  32 +
 .../src/test/java/org/gradle/SomeTests.java        |  40 ++
 .../build.gradle                                   |  28 +
 .../src/test/java/org/gradle/CatACTests.java       |  40 ++
 .../src/test/java/org/gradle/CatADTests.java       |  42 ++
 .../src/test/java/org/gradle/CatATests.java        |  40 ++
 .../src/test/java/org/gradle/CatBTests.java        |  40 ++
 .../src/test/java/org/gradle/CatCBTests.java       |  42 ++
 .../src/test/java/org/gradle/CatCTests.java        |  40 ++
 .../src/test/java/org/gradle/CatDTests.java        |  40 ++
 .../src/test/java/org/gradle/CatZTests.java        |  40 ++
 .../src/test/java/org/gradle/CategoryA.java        |  20 +
 .../src/test/java/org/gradle/CategoryB.java        |  20 +
 .../src/test/java/org/gradle/CategoryC.java        |  20 +
 .../src/test/java/org/gradle/CategoryD.java        |  20 +
 .../src/test/java/org/gradle/CategoryZ.java        |  20 +
 .../src/test/java/org/gradle/MixedTests.java       |  44 ++
 .../src/test/java/org/gradle/NoCatTests.java       |  38 +
 .../reportsUnloadableCategories/build.gradle       |  27 +
 .../src/test/java/org/gradle/SomeTestClass.java    |  13 +
 .../build.gradle                                   |  16 +
 .../src/test/java/org/gradle/SomeTest.java         |  12 +
 .../canHandleClassLevelIgnoredTests/build.gradle   |  23 +
 .../test/java/org/gradle/CustomIgnoredTest.java    |  70 ++
 .../src/test/java/org/gradle/IgnoredTest.java      |  12 +
 .../build.gradle                                   |   6 +-
 .../build.gradle                                   |   6 +-
 .../canRunTestsUsingJUnit3/build.gradle            |  25 +
 .../JUnitIntegrationTest/junit3Tests/build.gradle  |   9 -
 .../JUnitIntegrationTest/junit4Tests/build.gradle  |   9 -
 .../test/java/org/gradle/CustomIgnoredTest.java    |  71 --
 .../src/test/java/org/gradle/IgnoredTest.java      |  12 -
 .../src/test/java/org/gradle/Junit4Test.java       |  11 +-
 .../build.gradle                                   |   8 +-
 .../supportsTestCategories/build.gradle            |  16 +
 .../src/test/java/org/gradle/CategoryA.java        |   4 +
 .../src/test/java/org/gradle/CategoryB.java        |   4 +
 .../src/test/java/org/gradle/CategoryC.java        |   4 +
 .../src/test/java/org/gradle/SomeTest.java         |  27 +
 .../shared/build.gradle                            |  24 -
 .../src/test/groovy/org/gradle/TestNGTest.groovy   |  42 --
 .../standardOutputLogging/build.gradle             |  35 -
 .../org/gradle/TestNGStandardOutputTest.groovy     |  27 -
 .../org/gradle/api/distribution/Distribution.java  |  28 +-
 .../distribution/internal/DefaultDistribution.java |   8 +-
 .../internal/DefaultDistributionContainer.java     |  13 +-
 .../distribution/plugins/DistributionPlugin.groovy |  82 +--
 .../gradle/api/internal/java/WebApplication.java   |   2 +-
 .../internal/plugins/BuildConfigurationRule.java   |   2 +-
 .../org/gradle/api/internal/plugins/CleanRule.java |   2 +-
 .../api/internal/plugins/ProcessResources.java     |  21 +-
 .../internal/plugins/StartScriptGenerator.groovy   |  37 +
 .../gradle/api/internal/plugins/UploadRule.java    |   2 +-
 .../internal/tasks/DefaultBinariesContainer.java   |  27 -
 .../tasks/DefaultClassDirectoryBinary.java         | 109 ---
 .../api/internal/tasks/DefaultClasspath.java       |  38 -
 .../internal/tasks/DefaultFunctionalSourceSet.java |  34 -
 .../api/internal/tasks/DefaultJavaSourceSet.java   |  72 --
 .../internal/tasks/DefaultJvmBinaryContainer.java  |  36 -
 .../internal/tasks/DefaultProjectSourceSet.java    |  32 -
 .../api/internal/tasks/DefaultResourceSet.java     |  53 --
 .../api/internal/tasks/DefaultSourceSet.java       |   2 +-
 .../internal/tasks/DefaultSourceSetContainer.java  |   3 +
 .../api/internal/tasks/DefaultSourceSetOutput.java |   3 -
 .../internal/tasks/SourceSetCompileClasspath.java  |   5 +-
 .../compile/AntDependsStaleClassCleaner.groovy     |   6 +-
 .../tasks/compile/AntGroovyCompiler.groovy         |   2 -
 .../internal/tasks/compile/AntJavaCompiler.groovy  |   7 +-
 .../internal/tasks/compile/ApiGroovyCompiler.java  |   5 +-
 .../api/internal/tasks/compile/ArgCollector.java   |   2 +
 .../api/internal/tasks/compile/ArgWriter.java      |   7 +
 .../tasks/compile/CommandLineJavaCompiler.java     |   4 +-
 .../CommandLineJavaCompilerArgumentsGenerator.java |   7 +-
 .../tasks/compile/DefaultJavaCompilerFactory.java  |  26 +-
 .../tasks/compile/ExecSpecBackedArgCollector.java  |   5 +
 .../GroovyCompileTransformingClassLoader.java      | 124 ++++
 .../compile/InProcessJavaCompilerFactory.java      |   4 +-
 .../tasks/compile/IncrementalGroovyCompiler.java   |   2 +
 .../tasks/compile/IncrementalJavaCompiler.java     |   4 +-
 .../compile/IncrementalJavaCompilerSupport.java    |   2 +-
 .../tasks/compile/NoOpStaleClassCleaner.java       |   2 +
 .../tasks/compile/NormalizingGroovyCompiler.java   |   1 +
 .../tasks/compile/NormalizingJavaCompiler.java     |   1 +
 .../tasks/compile/SimpleStaleClassCleaner.java     |  38 -
 .../internal/tasks/compile/SimpleWorkResult.java   |  30 -
 .../internal/tasks/compile/StaleClassCleaner.java  |  53 --
 .../internal/tasks/compile/SunJavaCompiler.java    |   1 +
 .../tasks/compile/TransformingClassLoader.java     | 152 ----
 .../daemon/InProcessCompilerDaemonFactory.java     |   1 +
 .../tasks/compile/jdk6/Jdk6JavaCompiler.java       |  26 +-
 .../tasks/testing/DefaultJUnitXmlReport.java       |  41 ++
 .../tasks/testing/DefaultTestClassRunInfo.java     |   3 -
 .../tasks/testing/DefaultTestOutputEvent.java      |  28 +
 .../tasks/testing/DefaultTestTaskReports.java      |  46 ++
 .../internal/tasks/testing/TestClassProcessor.java |   2 -
 .../internal/tasks/testing/TestClassRunInfo.java   |   3 -
 .../api/internal/tasks/testing/TestFramework.java  |   5 +-
 .../detection/AbstractTestFrameworkDetector.java   |   3 -
 .../detection/ClassFileExtractionManager.java      |   2 -
 .../testing/detection/DefaultTestClassScanner.java |   2 -
 .../testing/detection/DefaultTestExecuter.java     |   2 -
 .../testing/detection/JarFilePackageListener.java  |   3 -
 .../testing/detection/JarFilePackageLister.java    |   3 -
 .../tasks/testing/detection/TestClassVisitor.java  |   2 -
 .../tasks/testing/detection/TestExecuter.java      |   3 -
 .../testing/detection/TestFrameworkDetector.java   |   3 -
 .../tasks/testing/junit/CategoryFilter.java        | 112 +++
 .../tasks/testing/junit/JUnitDetector.java         |   3 -
 .../internal/tasks/testing/junit/JUnitSpec.java    |  45 ++
 .../testing/junit/JUnitTestClassDetecter.java      |   3 -
 .../testing/junit/JUnitTestClassExecuter.java      |  40 +-
 .../testing/junit/JUnitTestClassProcessor.java     |  10 +-
 .../tasks/testing/junit/JUnitTestFramework.java    |  30 +-
 .../testing/junit/JUnitTestMethodDetecter.java     |   3 -
 .../tasks/testing/junit/report/AllTestResults.java |   8 +-
 .../testing/junit/report/ClassPageRenderer.java    |  16 +-
 .../testing/junit/report/ClassTestResults.java     |   8 +-
 .../testing/junit/report/DefaultTestReport.java    |   6 +-
 .../junit/report/LocaleSafeDecimalFormat.java      |   3 -
 .../testing/junit/report/OverviewPageRenderer.java |   2 +-
 .../testing/junit/report/PackageTestResults.java   |   8 +-
 .../junit/result/AggregateTestResultsProvider.java |  73 +-
 .../result/Binary2JUnitXmlReportGenerator.java     |   4 +-
 .../BinaryResultBackedTestResultsProvider.java     |  36 +-
 .../testing/junit/result/CachingFileWriter.java    | 100 ---
 .../junit/result/InMemoryTestResultsProvider.java  |  63 ++
 .../testing/junit/result/JUnitXmlResultWriter.java |  44 +-
 .../testing/junit/result/TestClassResult.java      |  14 +-
 .../testing/junit/result/TestMethodResult.java     |  17 +-
 .../junit/result/TestOutputAssociation.java        |  22 +
 .../testing/junit/result/TestOutputSerializer.java |  90 ---
 .../testing/junit/result/TestOutputStore.java      | 388 +++++++++++
 .../junit/result/TestReportDataCollector.java      |  74 +-
 .../testing/junit/result/TestResultSerializer.java |  43 +-
 .../testing/junit/result/TestResultsProvider.java  |  23 +-
 .../internal/tasks/testing/results/TestState.java  |   5 -
 .../testing/results/UnknownTestDescriptor.java     |  10 +-
 .../tasks/testing/testng/TestNGDetector.java       |   3 -
 .../testng/TestNGListenerAdapterFactory.java       |  25 +-
 .../testing/testng/TestNGTestClassDetecter.java    |   3 -
 .../testing/testng/TestNGTestClassProcessor.java   |   2 -
 .../tasks/testing/testng/TestNGTestFramework.java  |   5 +-
 .../testing/testng/TestNGTestMethodDetecter.java   |   3 -
 .../testng/TestNGTestResultProcessorAdapter.java   |  42 +-
 .../testng/UnrepresentableParameterException.java  |  32 +
 .../org/gradle/api/java/archives/Attributes.java   |   2 -
 .../api/java/archives/ManifestException.java       |   2 -
 .../java/archives/internal/DefaultAttributes.java  |   3 -
 .../java/archives/internal/DefaultManifest.java    |   3 -
 .../internal/DefaultManifestMergeDetails.java      |   3 -
 .../gradle/api/plugins/ApplicationPlugin.groovy    |  18 +-
 .../api/plugins/ApplicationPluginConvention.groovy |   7 +-
 .../groovy/org/gradle/api/plugins/BasePlugin.java  |   8 +-
 .../org/gradle/api/plugins/GroovyBasePlugin.java   |  91 ++-
 .../org/gradle/api/plugins/GroovyPlugin.java       |   4 +-
 .../org/gradle/api/plugins/JavaBasePlugin.java     |  52 +-
 .../org/gradle/api/plugins/JavaLanguagePlugin.java |  77 +-
 .../plugins/JavaLibraryDistributionPlugin.groovy   |   2 -
 .../groovy/org/gradle/api/plugins/JavaPlugin.java  |  12 +-
 .../gradle/api/plugins/JavaPluginConvention.groovy |   2 -
 .../org/gradle/api/plugins/JvmLanguagePlugin.java  | 128 ----
 .../org/gradle/api/plugins/LanguageBasePlugin.java |  60 --
 .../groovy/org/gradle/api/plugins/WarPlugin.java   |  10 +-
 .../org/gradle/api/tasks/BinariesContainer.java    |  27 -
 .../org/gradle/api/tasks/ClassDirectoryBinary.java |  49 --
 .../groovy/org/gradle/api/tasks/Classpath.java     |  28 -
 .../org/gradle/api/tasks/FunctionalSourceSet.java  |  27 -
 .../groovy/org/gradle/api/tasks/GroovyRuntime.java | 131 ++++
 .../groovy/org/gradle/api/tasks/JavaSourceSet.java |  24 -
 .../org/gradle/api/tasks/JvmBinaryContainer.java   |  27 -
 .../org/gradle/api/tasks/JvmLanguageSourceSet.java |  26 -
 .../org/gradle/api/tasks/LanguageSourceSet.java    |  33 -
 .../org/gradle/api/tasks/ProjectSourceSet.java     |  26 -
 .../groovy/org/gradle/api/tasks/ResourceSet.java   |  24 -
 .../org/gradle/api/tasks/SourceSetContainer.java   |   4 +
 .../tasks/application/CreateStartScripts.groovy    |  15 +-
 .../org/gradle/api/tasks/bundling/Jar.groovy       |  19 +-
 .../org/gradle/api/tasks/bundling/War.groovy       |   9 +-
 .../gradle/api/tasks/compile/AbstractOptions.java  |   6 +-
 .../gradle/api/tasks/compile/BaseForkOptions.java  |   3 -
 .../org/gradle/api/tasks/compile/Compile.java      |   9 +-
 .../gradle/api/tasks/compile/CompileOptions.java   |  21 +-
 .../org/gradle/api/tasks/compile/DebugOptions.java |   2 -
 .../gradle/api/tasks/compile/DependOptions.java    |   2 -
 .../org/gradle/api/tasks/compile/ForkOptions.java  |   2 -
 .../gradle/api/tasks/compile/GroovyCompile.java    |  11 +-
 .../api/tasks/compile/GroovyCompileOptions.java    |   3 -
 .../api/tasks/compile/GroovyForkOptions.java       |   2 -
 .../gradle/api/tasks/javadoc/AntGroovydoc.groovy   |   3 -
 .../org/gradle/api/tasks/javadoc/AntJavadoc.groovy |   3 +-
 .../org/gradle/api/tasks/javadoc/Groovydoc.java    |  16 +-
 .../org/gradle/api/tasks/javadoc/Javadoc.java      |   5 +-
 .../gradle/api/tasks/testing/JUnitXmlReport.java   |  39 ++
 .../groovy/org/gradle/api/tasks/testing/Test.java  | 258 ++++---
 .../gradle/api/tasks/testing/TestDescriptor.java   |   3 +
 .../org/gradle/api/tasks/testing/TestReport.java   |  34 +-
 .../gradle/api/tasks/testing/TestTaskReports.java  |  42 ++
 .../api/tasks/testing/junit/JUnitOptions.groovy    |  51 ++
 .../api/tasks/testing/junit/JUnitOptions.java      |  25 -
 .../api/tasks/testing/testng/TestNGOptions.groovy  |  15 +-
 .../org/gradle/api/tasks/wrapper/Wrapper.java      | 280 --------
 .../external/javadoc/CoreJavadocOptions.java       |   2 -
 .../external/javadoc/JavadocMemberLevel.java       |   2 -
 .../external/javadoc/JavadocOfflineLink.java       |   2 -
 .../external/javadoc/JavadocOptionFileOption.java  |   1 -
 .../external/javadoc/JavadocOutputLevel.java       |   2 -
 .../external/javadoc/MinimalJavadocOptions.java    |   2 -
 .../javadoc/OptionLessJavadocOptionFileOption.java |   1 -
 .../javadoc/StandardJavadocDocletOptions.java      |  12 +-
 .../internal/AbstractJavadocOptionFileOption.java  |   1 -
 .../AbstractListJavadocOptionFileOption.java       |   3 +-
 .../internal/BooleanJavadocOptionFileOption.java   |   2 -
 .../internal/EnumJavadocOptionFileOption.java      |   1 -
 .../internal/FileJavadocOptionFileOption.java      |   2 -
 .../internal/GroupsJavadocOptionFileOption.java    |   2 -
 .../javadoc/internal/JavadocExecHandleBuilder.java |   3 -
 .../javadoc/internal/JavadocOptionFile.java        |   3 -
 .../javadoc/internal/JavadocOptionFileWriter.java  |   3 -
 .../internal/JavadocOptionFileWriterContext.java   |   5 +-
 .../LinksOfflineJavadocOptionFileOption.java       |   7 +-
 .../MultilineStringsJavadocOptionFileOption.java   |   7 +-
 .../OptionLessStringsJavadocOptionFileOption.java  |   7 +-
 .../internal/PathJavadocOptionFileOption.java      |   5 +-
 .../internal/StringJavadocOptionFileOption.java    |   3 -
 .../internal/StringsJavadocOptionFileOption.java   |   7 +-
 .../META-INF/gradle-plugins/jvm-lang.properties    |   2 +-
 .../META-INF/gradle-plugins/lang-base.properties   |   2 +-
 .../api/internal/plugins/unixStartScript.txt       |   2 +-
 .../api/internal/plugins/windowsStartScript.txt    |   2 +-
 .../plugins/DistributionPluginTest.groovy          |   4 +-
 .../plugins/StartScriptGeneratorTest.groovy        |  99 +++
 .../api/internal/tasks/DefaultSourceSetTest.groovy |   2 +-
 .../compile/DefaultJavaCompilerFactoryTest.groovy  |   4 +-
 ...GroovyCompileTransformingClassLoaderTest.groovy |  96 +++
 .../compile/IncrementalJavaCompilerTest.groovy     |   1 +
 .../compile/SimpleStaleClassCleanerTest.groovy     |   1 +
 .../compile/TransformingClassLoaderTest.groovy     |  96 ---
 .../tasks/testing/AbstractTestFrameworkTest.java   |   3 -
 .../junit/JUnitTestClassProcessorTest.groovy       |   4 +-
 .../testing/junit/JUnitTestFrameworkTest.java      |  12 +-
 .../testing/junit/report/AllTestResultsTest.groovy |   4 +-
 .../junit/report/ClassTestResultsTest.groovy       |   4 +-
 .../junit/report/CompositeTestResultsTest.groovy   |   2 +-
 .../junit/report/DefaultTestReportTest.groovy      | 253 +------
 .../junit/report/LocaleSafeDecimalFormatTest.java  |   3 -
 .../Binary2JUnitXmlReportGeneratorSpec.groovy      |  23 +-
 .../junit/result/CachingFileWriterSpec.groovy      |  83 ---
 .../junit/result/JUnitXmlResultWriterSpec.groovy   | 101 ++-
 .../junit/result/TestClassResultSpec.groovy        |  11 +-
 .../junit/result/TestOutputSerializerTest.groovy   |  72 --
 .../junit/result/TestOutputStoreSpec.groovy        | 118 ++++
 .../result/TestReportDataCollectorSpec.groovy      |  75 +-
 .../junit/result/TestResultSerializerTest.groovy   |  25 +-
 .../tasks/testing/logging/SimpleTestResult.groovy  |  31 -
 .../testing/logging/TestEventLoggerTest.groovy     |   6 +-
 .../testing/results/DefaultTestResultTest.groovy   |   3 -
 .../testing/testng/TestNGTestFrameworkTest.groovy  |   7 +-
 .../archives/internal/DefaultAttributesTest.groovy |   5 +-
 .../internal/DefaultManifestMergeSpecTest.groovy   |   3 -
 .../archives/internal/DefaultManifestTest.groovy   |   3 -
 .../api/plugins/ApplicationPluginTest.groovy       |  27 +-
 .../api/plugins/BasePluginConventionTest.groovy    |   8 +-
 .../org/gradle/api/plugins/BasePluginTest.groovy   |  24 +-
 .../gradle/api/plugins/GroovyBasePluginTest.groovy |  31 +-
 .../org/gradle/api/plugins/GroovyPluginTest.groovy |   7 +-
 .../gradle/api/plugins/JavaBasePluginTest.groovy   |  61 +-
 .../api/plugins/JavaLanguagePluginTest.groovy      |  17 +-
 .../JavaLibraryDistributionPluginTest.groovy       |   4 +-
 .../api/plugins/JavaPluginConventionTest.groovy    |   7 +-
 .../org/gradle/api/plugins/JavaPluginTest.groovy   |  17 +-
 .../api/plugins/JvmLanguagePluginTest.groovy       |  64 +-
 .../api/plugins/LanguageBasePluginTest.groovy      |  29 +-
 .../org/gradle/api/plugins/WarPluginTest.groovy    |  12 +-
 .../org/gradle/api/tasks/GroovyRuntimeTest.groovy  | 116 ++++
 .../application/CreateStartScriptsTest.groovy      |   4 +-
 .../org/gradle/api/tasks/bundling/JarTest.groovy   |   4 +-
 .../org/gradle/api/tasks/bundling/WarTest.groovy   |   4 +-
 .../api/tasks/compile/CompileOptionsTest.groovy    |   8 +-
 .../api/tasks/compile/DebugOptionsTest.groovy      |   3 -
 .../api/tasks/compile/ForkOptionsTest.groovy       |   6 +-
 .../tasks/compile/GroovyCompileOptionsTest.groovy  |   6 +-
 .../api/tasks/compile/GroovyCompileTest.java       |  16 +-
 .../api/tasks/compile/GroovyForkOptionsTest.groovy |   6 +-
 .../gradle/api/tasks/compile/JavaCompileTest.java  |   3 -
 .../gradle/api/tasks/javadoc/GroovydocTest.java    |   7 +-
 .../org/gradle/api/tasks/javadoc/JavadocTest.java  |   2 +-
 .../testing/AbstractTestFrameworkOptionsTest.java  |   3 -
 .../gradle/api/tasks/testing/TestReportTest.groovy |   6 +-
 .../gradle/api/tasks/testing/TestTaskSpec.groovy   |   7 +-
 .../org/gradle/api/tasks/testing/TestTest.java     |  13 +-
 .../tasks/testing/testng/TestNGOptionsTest.groovy  |  13 +-
 .../org/gradle/api/tasks/wrapper/WrapperTest.java  | 164 -----
 .../javadoc/StandardJavadocDocletOptionsTest.java  |  20 +-
 .../BooleanJavadocOptionFileOptionTest.java        |   9 +-
 .../internal/EnumJavadocOptionFileOptionTest.java  |   7 +-
 .../internal/FileJavadocOptionFileOptionTest.java  |   7 +-
 .../GroupsJavadocOptionFileOptionTest.java         |   7 +-
 .../internal/JavadocExecHandleBuilderTest.groovy   |   9 +-
 .../javadoc/internal/JavadocOptionFileTest.java    |   3 -
 .../JavadocOptionFileWriterContextTest.java        |   7 +-
 .../LinksOfflineJavadocOptionFileOptionTest.java   |   7 +-
 ...ultilineStringsJavadocOptionFileOptionTest.java |   5 +-
 ...tionLessStringsJavadocOptionFileOptionTest.java |   5 +-
 .../internal/PathJavadocOptionFileOptionTest.java  |   7 +-
 .../StringJavadocOptionFileOptionTest.java         |   5 +-
 .../StringsJavadocOptionFileOptionTest.java        |   5 +-
 .../tasks/testing/BuildableTestMethodResult.groovy |  71 ++
 .../testing/BuildableTestResultsProvider.groovy    | 175 +++++
 .../tasks/testing/MethodTestOutputEvent.groovy     |  29 +
 .../internal/tasks/testing/SimpleTestResult.groovy |  35 +
 .../junit/report/HtmlTestResultsFixture.groovy     | 157 +++++
 .../api/tasks/compile/AbstractCompileTest.java     |   5 +-
 subprojects/publish/publish.gradle                 |   4 +-
 .../gradle/api/publish/PublicationContainer.java   |  55 +-
 .../gradle/api/publish/PublishingExtension.java    |  23 +
 .../internal/CompositePublicationFactory.java      |  39 --
 .../internal/DefaultPublicationContainer.java      |  25 +-
 .../internal/GroovyPublicationContainer.groovy     |  35 -
 .../ProjectDependencyPublicationResolver.java      |  57 ++
 .../internal/PublicationContainerInternal.java     |  23 -
 .../api/publish/internal/PublicationFactory.java   |  22 -
 .../api/publish/internal/PublicationInternal.java  |  23 +
 .../api/publish/plugins/PublishingPlugin.java      |  29 +-
 .../DefaultPublicationContainerTest.groovy         |  48 +-
 ...ProjectDependencyPublicationResolverTest.groovy | 134 ++++
 .../publish/plugins/PublishingPluginTest.groovy    |   4 +-
 subprojects/reporting/reporting.gradle             |   3 +-
 .../internal/TaskReportContainerIntegTest.groovy   |   0
 .../BuildDashboardPluginIntegrationTest.groovy     | 328 +++++++--
 .../api/reporting/BuildDashboardReports.java       |   8 +-
 .../gradle/api/reporting/ConfigurableReport.java   |  34 +
 .../org/gradle/api/reporting/DirectoryReport.java  |  47 ++
 .../api/reporting/GenerateBuildDashboard.java      |  58 +-
 .../groovy/org/gradle/api/reporting/Report.java    |  20 +-
 .../org/gradle/api/reporting/ReportContainer.java  |  14 +-
 .../groovy/org/gradle/api/reporting/Reporting.java |  40 +-
 .../gradle/api/reporting/ReportingExtension.java   |  15 +-
 .../org/gradle/api/reporting/SingleFileReport.java |  12 +-
 .../internal/BuildDashboardGenerator.java          |  70 +-
 .../internal/DefaultBuildDashboardReports.java     |  14 +-
 .../reporting/internal/DefaultReportContainer.java |   5 +-
 .../TaskGeneratedSingleDirectoryReport.java        |  45 ++
 .../reporting/plugins/BuildDashboardPlugin.groovy  |  58 --
 .../reporting/plugins/BuildDashboardPlugin.java    |  73 ++
 .../org/gradle/api/reporting/internal/style.css    |   3 +
 .../ReportingBasePluginConventionTest.groovy       |  65 ++
 .../api/plugins/ReportingBasePluginTest.groovy     |  48 ++
 .../reporting/GenerateBuildDashboardSpec.groovy    |   4 +-
 .../internal/BuildDashboardGeneratorSpec.groovy    |  22 +-
 subprojects/resources/resources.gradle             |  11 +
 .../org/gradle/internal/filestore/FileStore.java   |  32 +
 .../internal/filestore/FileStoreSearcher.java      |  27 +
 .../local/AbstractLocallyAvailableResource.java    |  55 ++
 .../local/DefaultLocallyAvailableResource.java     |  37 +
 .../resource/local/LocallyAvailableResource.java   |  31 +
 .../DefaultLocallyAvailableResourceTest.groovy     |  66 ++
 subprojects/scala/scala.gradle                     |   5 +-
 .../integtests/ScalaPluginGoodBehaviourTest.groovy |   0
 .../integtests/ScalaProjectIntegrationTest.java    |   0
 .../SamplesMixedJavaAndScalaIntegrationTest.groovy |   0
 ...plesScalaCustomizedLayoutIntegrationTest.groovy |   0
 .../SamplesScalaQuickstartIntegrationTest.groovy   |   0
 .../samples/SamplesScalaZincIntegrationTest.groovy |   0
 .../scala/ScalaBasePluginIntegrationTest.groovy    |  88 ++-
 .../JreJavaHomeScalaIntegrationTest.groovy         |  12 +-
 .../internal/tasks/scala/AntScalaCompiler.groovy   |   2 +-
 .../tasks/scala/IncrementalScalaCompiler.java      |   2 +
 .../tasks/scala/NormalizingScalaCompiler.java      |   4 +-
 .../tasks/scala/jdk6/ZincScalaCompiler.java        |   3 +-
 .../api/plugins/scala/ScalaBasePlugin.groovy       | 142 ++--
 .../gradle/api/plugins/scala/ScalaPlugin.groovy    |   5 +-
 .../org/gradle/api/tasks/ScalaRuntime.groovy       | 158 +++++
 .../org/gradle/api/tasks/scala/ScalaCompile.java   |   3 +-
 .../api/plugins/scala/ScalaBasePluginTest.groovy   | 140 +---
 .../api/plugins/scala/ScalaPluginTest.groovy       |   4 +-
 .../org/gradle/api/tasks/ScalaRuntimeTest.groovy   | 124 ++++
 .../gradle/api/tasks/scala/ScalaCompileTest.java   |   9 +-
 subprojects/signing/signing.gradle                 |   2 +-
 .../gradle/plugins/signing/SigningExtension.groovy |   8 +-
 .../plugins/signing/SigningPluginConvention.groovy |   8 +-
 .../plugins/signing/SigningProjectSpec.groovy      |   4 +-
 subprojects/sonar/sonar.gradle                     |   3 +-
 .../plugins/sonar/SonarSmokeIntegrationTest.groovy |  16 +-
 .../runner/SonarRunnerSmokeIntegrationTest.groovy  |  16 +-
 .../shared/javaProject/build.gradle                |   2 -
 .../shared/javaProjectWithJacoco/build.gradle      |   5 +
 .../org/gradle/test/javaProject/Production1.java   |   0
 .../org/gradle/test/javaProject/Production10.java  |   0
 .../org/gradle/test/javaProject/Production2.java   |   0
 .../org/gradle/test/javaProject/Production3.java   |   0
 .../org/gradle/test/javaProject/Production4.java   |   0
 .../org/gradle/test/javaProject/Production5.java   |   0
 .../org/gradle/test/javaProject/Production6.java   |   0
 .../org/gradle/test/javaProject/Production7.java   |   0
 .../org/gradle/test/javaProject/Production8.java   |   0
 .../org/gradle/test/javaProject/Production9.java   |   0
 .../gradle/test/javaProject/productionResource.xml |   0
 .../java/org/gradle/test/javaProject/Test1.java    |   0
 .../java/org/gradle/test/javaProject/Test10.java   |   0
 .../java/org/gradle/test/javaProject/Test2.java    |   0
 .../java/org/gradle/test/javaProject/Test3.java    |   0
 .../java/org/gradle/test/javaProject/Test4.java    |   0
 .../java/org/gradle/test/javaProject/Test5.java    |   0
 .../java/org/gradle/test/javaProject/Test6.java    |   0
 .../java/org/gradle/test/javaProject/Test7.java    |   0
 .../java/org/gradle/test/javaProject/Test8.java    |   0
 .../java/org/gradle/test/javaProject/Test9.java    |   0
 .../org/gradle/test/javaProject/testResource.xml   |   0
 .../shared/settings.gradle                         |   2 +-
 .../shared/javaProject/build.gradle                |   2 -
 .../shared/javaProjectWithJacoco/build.gradle      |   5 +
 .../org/gradle/test/javaProject/Production1.java   |   0
 .../org/gradle/test/javaProject/Production10.java  |   0
 .../org/gradle/test/javaProject/Production2.java   |   0
 .../org/gradle/test/javaProject/Production3.java   |   0
 .../org/gradle/test/javaProject/Production4.java   |   0
 .../org/gradle/test/javaProject/Production5.java   |   0
 .../org/gradle/test/javaProject/Production6.java   |   0
 .../org/gradle/test/javaProject/Production7.java   |   0
 .../org/gradle/test/javaProject/Production8.java   |   0
 .../org/gradle/test/javaProject/Production9.java   |   0
 .../gradle/test/javaProject/productionResource.xml |   0
 .../java/org/gradle/test/javaProject/Test1.java    |   0
 .../java/org/gradle/test/javaProject/Test10.java   |   0
 .../java/org/gradle/test/javaProject/Test2.java    |   0
 .../java/org/gradle/test/javaProject/Test3.java    |   0
 .../java/org/gradle/test/javaProject/Test4.java    |   0
 .../java/org/gradle/test/javaProject/Test5.java    |   0
 .../java/org/gradle/test/javaProject/Test6.java    |   0
 .../java/org/gradle/test/javaProject/Test7.java    |   0
 .../java/org/gradle/test/javaProject/Test8.java    |   0
 .../java/org/gradle/test/javaProject/Test9.java    |   0
 .../org/gradle/test/javaProject/testResource.xml   |   0
 .../shared/settings.gradle                         |   2 +-
 .../gradle/api/plugins/sonar/SonarAnalyze.groovy   |   2 +-
 .../gradle/api/plugins/sonar/SonarPlugin.groovy    |  17 +-
 .../api/sonar/runner/SonarRunnerPlugin.groovy      |  34 +-
 .../api/plugins/sonar/SonarAnalyzeTest.groovy      |   2 +-
 .../api/plugins/sonar/SonarPluginTest.groovy       |  27 +-
 .../api/sonar/runner/SonarRunnerPluginTest.groovy  |  30 +-
 .../tooling/AutoTestedSamplesToolingApiTest.groovy |   5 +-
 .../ToolingApiClasspathIntegrationTest.groovy      |   9 +-
 .../tooling/ToolingApiIntegrationTest.groovy       |  14 -
 ...lingApiUnsupportedVersionIntegrationTest.groovy |  62 ++
 .../tooling/fixture/ConfigurableOperation.groovy   |   3 -
 .../fixture/ExternalToolingApiDistribution.groovy  |  18 +-
 .../tooling/fixture/IncludeAllPermutations.java    |  23 -
 .../tooling/fixture/MaxTargetGradleVersion.java    |  25 -
 .../tooling/fixture/MinTargetGradleVersion.java    |  25 -
 .../tooling/fixture/MinToolingApiVersion.java      |  25 -
 .../tooling/fixture/TargetGradleVersion.java       |  31 +
 .../TestClasspathToolingApiDistribution.groovy     |   7 +-
 .../integtests/tooling/fixture/ToolingApi.groovy   |  36 +-
 .../ToolingApiCompatibilitySuiteRunner.groovy      |  74 +-
 .../tooling/fixture/ToolingApiDistribution.groovy  |   6 +-
 .../fixture/ToolingApiDistributionResolver.groovy  |  30 +-
 .../tooling/fixture/ToolingApiSpecification.groovy |  27 +-
 .../tooling/fixture/ToolingApiVersion.java         |  31 +
 .../m3/ToolingApiLoggingCrossVersionSpec.groovy    | 103 +++
 ...piEclipseLinkedResourcesCrossVersionSpec.groovy |  11 +-
 ...ngApiEclipseMinimalModelCrossVersionSpec.groovy |   4 +-
 ...EclipseModelWithFlatRepoCrossVersionSpec.groovy |   4 +-
 ...ToolingApiBuildExecutionCrossVersionSpec.groovy |   8 +-
 ...ildableEclipseModelFixesCrossVersionSpec.groovy |   8 +-
 .../ToolingApiEclipseModelCrossVersionSpec.groovy  |   6 +-
 .../ToolingApiGradleProjectCrossVersionSpec.groovy |   6 +-
 ...orsProjectCustomizationsCrossVersionSpec.groovy |   8 +-
 .../m5/ToolingApiIdeaModelCrossVersionSpec.groovy  |   8 +-
 .../m5/ToolingApiModelCrossVersionSpec.groovy      |   8 +-
 ...ReceivingStandardStreamsCrossVersionSpec.groovy |   8 +-
 ...UnsupportedModelFeedbackCrossVersionSpec.groovy |  38 +
 .../BuildEnvironmentModelCrossVersionSpec.groovy   |   8 +-
 .../ConsumingStandardInputCrossVersionSpec.groovy  |   8 +-
 ...adlePropertiesToolingApiCrossVersionSpec.groovy |   8 +-
 .../m8/JavaConfigurabilityCrossVersionSpec.groovy  |   8 +-
 ...rictLongRunningOperationCrossVersionSpec.groovy |  25 +-
 .../ToolingApiEclipseModelCrossVersionSpec.groovy  |  10 +-
 .../m8/ToolingApiLoggingCrossVersionSpec.groovy    |  63 +-
 .../m8/UnknownModelFeedbackCrossVersionSpec.groovy |  42 --
 ...sionOnlyBuildEnvironmentCrossVersionSpec.groovy |  10 +-
 .../m9/DaemonErrorFeedbackCrossVersionSpec.groovy  |  10 +-
 .../M9JavaConfigurabilityCrossVersionSpec.groovy   |  10 +-
 ...singCommandLineArgumentsCrossVersionSpec.groovy |  13 +-
 .../DependencyMetaDataCrossVersionSpec.groovy      |   8 +-
 .../r12rc1/BuildModelCrossVersionSpec.groovy       |   8 +-
 .../ProjectOutcomesModuleCrossVersionSpec.groovy   |   8 +-
 ...pportedOperationFeedbackCrossVersionSpec.groovy |  10 +-
 ...ApiInitScriptCrossVersionIntegrationTest.groovy |   4 +-
 ...ningCommandLineArgumentsCrossVersionSpec.groovy |   8 +-
 ...ApiConfigurationOnDemandCrossVersionSpec.groovy |   8 +-
 .../gradle/integtests/tooling/r16/CustomModel.java |  31 +
 .../r16/CustomToolingModelCrossVersionSpec.groovy  |  81 +++
 ...knownCustomModelFeedbackCrossVersionSpec.groovy |  58 ++
 .../integtests/tooling/r18/BrokenAction.java       |  29 +
 .../tooling/r18/BuildActionCrossVersionSpec.groovy | 104 +++
 .../r18/BuildScriptModelCrossVersionSpec.groovy    |  73 ++
 .../gradle/integtests/tooling/r18/CustomModel.java |  27 +
 .../integtests/tooling/r18/FetchCustomModel.java   |  31 +
 .../integtests/tooling/r18/FetchIdeaModel.java     |  27 +
 .../integtests/tooling/r18/FetchUnknownModel.java  |  32 +
 .../r18/GradleBuildModelCrossVersionSpec.groovy    |  57 ++
 .../gradle/integtests/tooling/r18/NullAction.java  |  26 +
 .../r18/ProjectLevelModelCrossVersionSpec.groovy   | 111 +++
 .../r18/UseGradleBuildToFetchProjectModel.java     |  37 +
 .../r18/UseOtherTypesToFetchProjectModel.java      |  56 ++
 .../main/java/org/gradle/tooling/BuildAction.java  |  41 ++
 .../org/gradle/tooling/BuildActionExecuter.java    |  60 ++
 .../tooling/BuildActionFailureException.java       |  31 +
 .../java/org/gradle/tooling/BuildController.java   | 112 +++
 .../java/org/gradle/tooling/BuildException.java    |   4 +-
 .../java/org/gradle/tooling/BuildLauncher.java     |  20 +-
 .../gradle/tooling/GradleConnectionException.java  |   2 +
 .../org/gradle/tooling/LongRunningOperation.java   |  26 +-
 .../main/java/org/gradle/tooling/ModelBuilder.java |  27 +-
 .../java/org/gradle/tooling/ProjectConnection.java |  59 +-
 .../org/gradle/tooling/UnknownModelException.java  |   4 +
 .../tooling/UnsupportedVersionException.java       |   2 +
 .../UnsupportedBuildArgumentException.java         |   6 +-
 .../tooling/internal/adapter/CollectionMapper.java |  50 ++
 .../internal/adapter/CompatibleIntrospector.java   |  76 ++
 .../tooling/internal/adapter/MethodInvocation.java |  79 +++
 .../tooling/internal/adapter/MethodInvoker.java    |  21 +
 .../internal/adapter/NoOpMethodInvoker.java        |  24 +
 .../internal/adapter/ProtocolToModelAdapter.java   | 489 +++++++++++++
 .../internal/adapter/SourceObjectMapping.java      |  28 +
 .../internal/adapter/TargetTypeProvider.java       |  26 +
 .../internal/build/DefaultBuildEnvironment.java    |  19 +-
 .../build/VersionOnlyBuildEnvironment.java         |  19 +-
 .../consumer/AbstractLongRunningOperation.java     |  70 ++
 .../internal/consumer/BlockingResultHandler.java   |  16 +-
 .../internal/consumer/ConnectionFactory.java       |  21 +-
 .../internal/consumer/ConnectorServices.java       |   7 +-
 .../consumer/DefaultBuildActionExecuter.java       |  63 ++
 .../internal/consumer/DefaultBuildLauncher.java    |  81 +--
 .../internal/consumer/DefaultGradleConnector.java  |   4 -
 .../internal/consumer/DefaultModelBuilder.java     | 107 +--
 .../consumer/DefaultProjectConnection.java         |  40 +-
 .../tooling/internal/consumer/LoggingProvider.java |   3 -
 .../tooling/internal/consumer/ModelProvider.java   |  80 ---
 .../internal/consumer/SynchronizedLogging.java     |   4 +-
 .../internal/consumer/async/AsyncConnection.java   |  30 -
 .../async/AsyncConsumerActionExecutor.java         |  39 ++
 .../consumer/async/DefaultAsyncConnection.java     |  88 ---
 .../async/DefaultAsyncConsumerActionExecutor.java  |  66 ++
 .../connection/AbstractConsumerConnection.java     |  13 +-
 .../AbstractPost12ConsumerConnection.java          |  38 +
 .../AbstractPre12ConsumerConnection.java           |  62 ++
 .../connection/ActionAwareConsumerConnection.java  |  59 ++
 .../consumer/connection/AdaptedConnection.java     |  55 --
 .../BuildActionRunnerBackedConsumerConnection.java |  61 +-
 .../connection/BuildControllerAdapter.java         |  81 +++
 ...ConnectionVersion4BackedConsumerConnection.java | 125 ++++
 .../consumer/connection/ConsumerAction.java        |  25 +
 .../connection/ConsumerActionExecutor.java         |  29 +
 .../consumer/connection/ConsumerConnection.java    |   8 +-
 .../connection/ConsumerConnectionMetadata.java     |  44 --
 ...InternalConnectionBackedConsumerConnection.java |  53 +-
 .../consumer/connection/LazyConnection.java        | 143 ----
 .../connection/LazyConsumerActionExecutor.java     | 115 +++
 .../connection/LoggingInitializerConnection.java   |  54 --
 .../LoggingInitializerConsumerActionExecutor.java  |  46 ++
 .../ModelBuilderBackedConsumerConnection.java      |  69 ++
 .../connection/NoToolingApiConnection.java         |  49 ++
 .../connection/ProgressLoggingConnection.java      | 102 ---
 .../ProgressLoggingConsumerActionExecutor.java     |  85 +++
 .../converters/ConsumerTargetTypeProvider.java     |  47 ++
 .../converters/GradleProjectConverter.java         |   3 -
 .../converters/GradleProjectMixInHandler.java      |  30 +
 .../converters/PropertyHandlerFactory.java         |  44 ++
 .../loader/DefaultToolingImplementationLoader.java |  42 +-
 .../SynchronizedToolingImplementationLoader.java   |   3 -
 .../parameters/ConsumerOperationParameters.java    |   4 +-
 .../protocoladapter/ConsumerPropertyHandler.java   |  41 --
 .../consumer/protocoladapter/MethodInvocation.java |  76 --
 .../consumer/protocoladapter/MethodInvoker.java    |  21 -
 .../protocoladapter/ProtocolToModelAdapter.java    | 358 ----------
 .../protocoladapter/TargetTypeProvider.java        |  55 --
 .../internal/consumer/versioning/ModelMapping.java | 118 +++-
 .../consumer/versioning/VersionDetails.java        |  52 +-
 .../eclipse/DefaultEclipseExternalDependency.java  |  54 --
 .../eclipse/DefaultEclipseLinkedResource.java      |  55 --
 .../internal/eclipse/DefaultEclipseProject.java    | 134 ----
 .../eclipse/DefaultEclipseProjectDependency.java   |  44 --
 .../eclipse/DefaultEclipseSourceDirectory.java     |  44 --
 .../internal/eclipse/DefaultEclipseTask.java       |  56 --
 .../internal/gradle/BasicGradleProject.java        |  80 +++
 .../internal/gradle/DefaultGradleBuild.java        |  43 ++
 .../gradle/DefaultGradleModuleVersion.java         |   3 -
 .../internal/gradle/DefaultGradleProject.java      |  12 +-
 .../internal/gradle/DefaultGradleScript.java       |  34 +
 .../tooling/internal/gradle/DefaultGradleTask.java |   3 -
 .../internal/gradle/GradleProjectIdentity.java     |  21 +
 .../internal/idea/DefaultIdeaCompilerOutput.java   |  68 --
 .../internal/idea/DefaultIdeaContentRoot.java      |  83 ---
 .../internal/idea/DefaultIdeaDependencyScope.java  |  67 --
 .../internal/idea/DefaultIdeaLanguageLevel.java    |  85 ---
 .../tooling/internal/idea/DefaultIdeaModule.java   | 125 ----
 .../internal/idea/DefaultIdeaModuleDependency.java |  69 --
 .../tooling/internal/idea/DefaultIdeaProject.java  | 117 ----
 .../DefaultIdeaSingleEntryLibraryDependency.java   | 103 ---
 .../internal/idea/DefaultIdeaSourceDirectory.java  |  46 --
 .../outcomes/DefaultGradleBuildOutcome.java        |  46 --
 .../outcomes/DefaultGradleFileBuildOutcome.java    |  42 --
 .../internal/outcomes/DefaultProjectOutcomes.java  |  80 ---
 .../internal/protocol/BuildActionRunner.java       |  16 +
 .../internal/protocol/BuildExceptionVersion1.java  |   5 +-
 .../protocol/BuildOperationParametersVersion1.java |  23 +
 .../tooling/internal/protocol/BuildParameters.java |   4 +-
 .../internal/protocol/BuildParametersVersion1.java |   7 +
 .../internal/protocol/ConfigurableConnection.java  |  14 +
 .../internal/protocol/ConnectionVersion4.java      |  59 +-
 .../protocol/InternalBasicIdeaProject.java         |   4 +-
 .../internal/protocol/InternalBuildAction.java     |  36 +
 .../protocol/InternalBuildActionExecutor.java      |  51 ++
 .../InternalBuildActionFailureException.java       |  30 +
 .../internal/protocol/InternalBuildController.java |  53 ++
 .../protocol/InternalBuildEnvironment.java         |   3 +-
 .../internal/protocol/InternalConnection.java      |  17 +-
 .../internal/protocol/InternalGradleProject.java   |   2 +
 .../internal/protocol/InternalIdeaProject.java     |   2 +
 .../internal/protocol/InternalProjectOutcomes.java |   7 +-
 .../protocol/InternalProtocolInterface.java        |  12 +-
 .../internal/protocol/InternalTestModel.java       |  24 -
 .../InternalUnsupportedModelException.java         |  31 +
 .../LongRunningOperationParametersVersion1.java    |   8 +
 .../tooling/internal/protocol/ModelBuilder.java    |  51 ++
 .../tooling/internal/protocol/ModelIdentifier.java |  42 ++
 .../tooling/internal/protocol/ProjectVersion3.java |  17 +
 .../eclipse/EclipseLinkedResourceVersion1.java     |   2 -
 .../protocol/eclipse/EclipseProjectVersion3.java   |   2 +
 .../HierarchicalEclipseProjectVersion1.java        |   2 +
 .../internal/reflect/CompatibleIntrospector.java   |  71 --
 .../org/gradle/tooling/model/BuildableElement.java |   1 +
 .../java/org/gradle/tooling/model/Element.java     |   2 +
 .../gradle/tooling/model/ExternalDependency.java   |   2 +-
 .../gradle/tooling/model/GradleModuleVersion.java  |   2 +-
 .../org/gradle/tooling/model/GradleProject.java    |  21 +-
 .../gradle/tooling/model/HierarchicalElement.java  |   2 +
 .../main/java/org/gradle/tooling/model/Model.java  |   1 +
 .../main/java/org/gradle/tooling/model/Task.java   |   6 +
 .../tooling/model/build/BuildEnvironment.java      |   7 +-
 .../tooling/model/build/GradleEnvironment.java     |   2 +
 .../tooling/model/build/JavaEnvironment.java       |   4 +
 .../gradle/tooling/model/build/package-info.java   |   2 +-
 .../model/eclipse/EclipseLinkedResource.java       |  10 +-
 .../tooling/model/eclipse/EclipseProject.java      |  15 +-
 .../gradle/tooling/model/eclipse/EclipseTask.java  |   5 +-
 .../model/eclipse/HierarchicalEclipseProject.java  |   5 +
 .../gradle/tooling/model/eclipse/package-info.java |   2 +-
 .../tooling/model/gradle/BasicGradleProject.java   |  69 ++
 .../gradle/tooling/model/gradle/GradleBuild.java   |  42 ++
 .../gradle/tooling/model/gradle/GradleScript.java  |  40 ++
 .../gradle/tooling/model/gradle/package-info.java  |  20 +
 .../tooling/model/idea/BasicIdeaProject.java       |   2 +
 .../org/gradle/tooling/model/idea/IdeaModule.java  |   9 +-
 .../org/gradle/tooling/model/idea/IdeaProject.java |   7 +-
 .../gradle/tooling/model/idea/package-info.java    |   2 +-
 .../gradle/tooling/model/internal/Exceptions.java  |  41 +-
 .../gradle/tooling/model/internal/TestModel.java   |  26 -
 .../org/gradle/tooling/model/package-info.java     |   2 +-
 .../tooling/fixture/GradleVersionSpecTest.groovy   | 146 ++++
 .../internal/adapter/CollectionMapperTest.groovy   |  49 ++
 .../adapter/CompatibleIntrospectorTest.groovy      |  79 +++
 .../adapter/ProtocolToModelAdapterTest.groovy      | 467 +++++++++++++
 .../internal/consumer/ConnectionFactoryTest.groovy |  17 +-
 .../internal/consumer/ConnectorServicesTest.groovy |   3 -
 .../consumer/DefaultBuildActionExecuterTest.groovy | 164 +++++
 .../consumer/DefaultBuildLauncherTest.groovy       | 176 +++--
 .../consumer/DefaultModelBuilderTest.groovy        | 159 +++--
 .../consumer/DefaultProjectConnectionTest.groovy   |  30 +-
 .../consumer/DistributionFactoryTest.groovy        |   6 +-
 .../consumer/ProtocolToModelAdapterTest.groovy     |  68 --
 .../consumer/SynchronizedLoggingTest.groovy        |   3 -
 .../DefaultAsyncConsumerActionExecutorTest.groovy  |  81 +++
 .../ActionAwareConsumerConnectionTest.groovy       |  75 ++
 .../connection/AdaptedConnectionTest.groovy        |  50 --
 ...ActionRunnerBackedConsumerConnectionTest.groovy |  91 ++-
 .../connection/BuildControllerAdapterTest.groovy   | 122 ++++
 ...tionVersion4BackedConsumerConnectionTest.groovy | 258 +++++++
 ...alConnectionBackedConsumerConnectionTest.groovy |  94 ++-
 .../consumer/connection/LazyConnectionTest.groovy  | 107 ---
 .../LazyConsumerActionExecutorTest.groovy          | 106 +++
 ...ModelBuilderBackedConsumerConnectionTest.groovy | 109 +++
 .../ProgressLoggingConnectionTest.groovy           |  55 --
 ...rogressLoggingConsumerActionExecutorTest.groovy |  55 ++
 .../DefaultToolingImplementationLoaderTest.groovy  |  94 ++-
 ...chronizedToolingImplementationLoaderTest.groovy |   3 -
 .../ConsumerOperationParametersTest.groovy         |   3 -
 .../ProtocolToModelAdapterTest.groovy              | 271 --------
 .../consumer/versioning/ModelMappingTest.groovy    | 115 +++
 .../eclipse/DefaultEclipseProjectTest.groovy       |  27 -
 .../reflect/CompatibleIntrospectorTest.groovy      |  59 --
 .../tooling/fixture/GradleVersionSpec.java         |  83 +++
 subprojects/tooling-api/tooling-api.gradle         |   7 +-
 .../integtests/FavoritesIntegrationTest.java       |   2 -
 .../integtests/LiveOutputIntegrationTest.groovy    |   5 -
 ...projectProjectAndTaskListIntegrationTest.groovy |   3 -
 .../gradle/foundation/CommandLineAssistant.java    |   2 -
 .../org/gradle/foundation/PathParserPortion.java   |   2 -
 .../org/gradle/foundation/ProjectConverter.java    |   2 -
 .../java/org/gradle/foundation/ProjectView.java    |   2 -
 .../main/java/org/gradle/foundation/TaskView.java  |   2 -
 .../gradle/foundation/common/ListReorderer.java    |   2 -
 .../org/gradle/foundation/common/ObserverLord.java |   3 -
 .../gradle/foundation/common/ReorderableList.java  |   3 -
 .../gradle/foundation/ipc/basic/ClientProcess.java |   2 -
 .../gradle/foundation/ipc/basic/ExecutionInfo.java |   4 -
 .../gradle/foundation/ipc/basic/MessageObject.java |   2 -
 .../foundation/ipc/basic/ObjectSocketWrapper.java  |   2 -
 .../ipc/basic/ProcessLauncherServer.java           |   2 -
 .../org/gradle/foundation/ipc/basic/Server.java    |   2 -
 .../ipc/gradle/AbstractGradleServerProtocol.java   |   2 -
 .../gradle/ExecuteGradleCommandClientProtocol.java |   2 -
 .../gradle/ExecuteGradleCommandServerProtocol.java |   2 -
 .../gradle/foundation/ipc/gradle/IPCUtilities.java |   2 -
 .../ipc/gradle/KillGradleClientProtocol.java       |   2 -
 .../ipc/gradle/KillGradleServerProtocol.java       |   2 -
 .../foundation/ipc/gradle/ProtocolConstants.java   |   2 -
 .../ipc/gradle/TaskListClientProtocol.java         |   2 -
 .../ipc/gradle/TaskListServerProtocol.java         |   2 -
 .../org/gradle/foundation/output/FileLink.java     |   2 -
 .../foundation/output/FileLinkDefinitionLord.java  |   2 -
 .../gradle/foundation/output/LiveOutputParser.java |   2 -
 .../org/gradle/foundation/output/OutputParser.java |   2 -
 .../definitions/ExtensionFileLinkDefinition.java   |   2 -
 .../output/definitions/FileLinkDefinition.java     |   2 -
 .../OptionalLineNumberFileLinkDefinition.java      |   2 -
 .../definitions/PrefixedFileLinkDefinition.java    |   2 -
 .../definitions/TestReportFileLinkDefinition.java  |   2 -
 .../gradle/foundation/queue/ExecutionQueue.java    |   2 -
 .../visitors/AllProjectsAndTasksVisitor.java       |  10 -
 .../visitors/TaskTreePopulationVisitor.java        |  10 -
 .../visitors/UniqueNameProjectAndTaskVisitor.java  |   4 -
 .../CommandLineArgumentAlteringListener.java       |   2 -
 .../gradleplugin/foundation/DOM4JSerializer.java   |   2 -
 .../gradleplugin/foundation/Dom4JUtility.java      |   1 -
 .../foundation/ExtensionFileFilter.java            |   2 -
 .../gradleplugin/foundation/GradlePluginLord.java  |   2 -
 .../foundation/favorites/FavoriteTask.java         |   2 -
 .../foundation/favorites/FavoritesEditor.java      |   2 -
 .../favorites/FavoritesSerializable.java           |   5 +-
 .../filters/AllowAllProjectAndTaskFilter.java      |   2 -
 .../foundation/filters/BasicFilterEditor.java      |   2 -
 .../filters/BasicProjectAndTaskFilter.java         |   2 -
 .../foundation/filters/ProjectAndTaskFilter.java   |   3 -
 .../foundation/request/AbstractRequest.java        |   2 -
 .../foundation/request/ExecutionRequest.java       |   2 -
 .../foundation/request/RefreshTaskListRequest.java |   2 -
 .../gradleplugin/foundation/request/Request.java   |   2 -
 .../foundation/runner/GradleRunner.java            |   2 -
 .../foundation/search/BasicTextSearchCriteria.java |   2 -
 .../foundation/search/TextBlockSearchEditor.java   |   3 -
 .../foundation/settings/DOM4JSettingsNode.java     |   2 -
 .../foundation/settings/SettingsNode.java          |   2 -
 .../foundation/settings/SettingsSerializable.java  |   3 -
 .../userinterface/AlternateUIInteraction.java      |   3 -
 .../swing/common/BorderlessImageButton.java        |   2 -
 .../swing/common/BorderlessImageToggleButton.java  |   3 -
 .../swing/common/BorderlessUtility.java            |   2 -
 .../swing/common/PreferencesAssistant.java         |   2 -
 .../userinterface/swing/common/SearchPanel.java    |  21 +-
 .../swing/common/TextPaneSearchInteraction.java    |   4 +-
 .../swing/generic/AbstractGradleUIInstance.java    |   3 -
 .../userinterface/swing/generic/BasicGradleUI.java |  10 -
 .../swing/generic/DualPaneUIInstance.java          |   2 -
 .../swing/generic/MainGradlePanel.java             |   2 -
 .../userinterface/swing/generic/OutputPanel.java   |  26 +-
 .../swing/generic/OutputPanelLord.java             |   9 +-
 .../userinterface/swing/generic/OutputTab.java     |  16 +-
 .../swing/generic/SinglePaneUIInstance.java        |   2 -
 .../SwingAddMultipleFavoritesInteraction.java      |   2 -
 .../generic/SwingEditFavoriteInteraction.java      |  10 +-
 .../swing/generic/SwingExportInteraction.java      |   8 +-
 .../swing/generic/SwingImportInteraction.java      |   2 -
 .../swing/generic/TaskTreeComponent.java           |   6 +-
 .../userinterface/swing/generic/Utility.java       |  18 +-
 .../generic/filter/AbstractFilterEditorPanel.java  |   2 -
 .../generic/filter/ProjectAndTaskFilterDialog.java |   2 -
 .../swing/generic/tabs/CommandLineTab.java         |   2 -
 .../swing/generic/tabs/FavoriteTasksTab.java       |   2 -
 .../swing/generic/tabs/GradleTab.java              |   3 -
 .../userinterface/swing/generic/tabs/SetupTab.java |   2 -
 .../swing/generic/tabs/TaskTreeTab.java            |   3 -
 .../swing/standalone/Application.java              |   2 -
 .../swing/standalone/BlockingApplication.java      |   2 -
 .../openapi/wrappers/RunnerWrapperFactory.java     |   3 -
 .../gradle/openapi/wrappers/UIWrapperFactory.java  |   2 -
 .../foundation/GradleInterfaceWrapperVersion1.java |   2 -
 .../foundation/GradleInterfaceWrapperVersion2.java |   2 -
 .../wrappers/foundation/ProjectWrapper.java        |   2 -
 .../foundation/RequestObserverWrapper.java         |   2 -
 .../wrappers/foundation/RequestWrapper.java        |   2 -
 .../openapi/wrappers/foundation/TaskWrapper.java   |   2 -
 .../foundation/favorites/FavoriteTaskWrapper.java  |   2 -
 .../favorites/FavoritesEditorWrapper.java          |   2 -
 .../runner/GradleRunnerInteractionWrapper.java     |   2 -
 .../wrappers/runner/GradleRunnerWrapper.java       |   2 -
 .../wrappers/ui/AbstractOpenAPIUIWrapper.java      |   3 -
 .../ui/AlternateUIInteractionVersionWrapper.java   |   2 -
 ...CommandLineArgumentAlteringListenerWrapper.java |   2 -
 .../openapi/wrappers/ui/DualPaneUIWrapper.java     |   2 -
 .../wrappers/ui/GradleTabVersionWrapper.java       |   2 -
 .../openapi/wrappers/ui/OutputObserverWrapper.java |   2 -
 .../openapi/wrappers/ui/OutputUILordWrapper.java   |   2 -
 .../wrappers/ui/SettingsNodeVersionWrapper.java    |   2 -
 .../openapi/wrappers/ui/SinglePaneUIWrapper.java   |   2 -
 .../org/gradle/foundation/BuildInformation.java    |   7 +-
 .../gradle/foundation/CommandLineParsingTest.java  |   2 -
 .../gradle/foundation/DOM4JSettingsNodeTest.java   |   2 -
 .../org/gradle/foundation/FavoritesTest.java       |   2 -
 .../org/gradle/foundation/FileLinkTests.java       |   1 -
 .../groovy/org/gradle/foundation/FilterTest.java   |   2 -
 .../gradle/foundation/LiveOutputParserTests.java   |   2 -
 .../groovy/org/gradle/foundation/TestUtility.java  |   4 +-
 .../foundation/TextBlockSearchEditorTests.java     |   2 -
 subprojects/ui/ui.gradle                           |   2 +-
 .../WrapperConcurrentDownloadTest.groovy           |  89 +++
 .../WrapperGenerationIntegrationTest.groovy        |  49 ++
 .../integtests/WrapperHttpIntegrationTest.groovy   | 148 ++++
 .../WrapperProjectIntegrationTest.groovy           | 125 +---
 .../org/gradle/integtests/WrapperSetup.groovy      |  28 +
 .../WrapperUserHomeIntegrationTest.groovy          |  72 ++
 .../org/gradle/wrapper/BootstrapMainStarter.java   |   3 -
 .../src/main/java/org/gradle/wrapper/Download.java |   7 -
 .../gradle/wrapper/ExclusiveFileAccessManager.java |  95 +++
 .../java/org/gradle/wrapper/GradleWrapperMain.java |  51 +-
 .../main/java/org/gradle/wrapper/IDownload.java    |   3 -
 .../src/main/java/org/gradle/wrapper/Install.java  | 111 +--
 .../java/org/gradle/wrapper/PathAssembler.java     |   3 -
 .../gradle/wrapper/SystemPropertiesHandler.java    |   3 -
 .../org/gradle/wrapper/WrapperConfiguration.java   |  21 -
 .../java/org/gradle/wrapper/WrapperExecutor.java   |   3 -
 .../groovy/org/gradle/wrapper/DownloadTest.groovy  |   3 -
 .../groovy/org/gradle/wrapper/InstallTest.groovy   |  97 +--
 .../org/gradle/wrapper/PathAssemblerTest.java      |   3 -
 .../wrapper/SystemPropertiesHandlerTest.groovy     |   3 -
 subprojects/wrapper/wrapper.gradle                 |   3 +-
 version.txt                                        |   2 +-
 3896 files changed, 102930 insertions(+), 50908 deletions(-)

diff --git a/build.gradle b/build.gradle
index d5bca86..df49a77 100644
--- a/build.gradle
+++ b/build.gradle
@@ -28,23 +28,26 @@ buildTypes {
     sanityCheck "classes", "doc:checkstyleApi", "codeQuality", "docs:check"
 
     // The minimum to be run before check-in
-    preCommitBuild "doc:checkstyleApi", "docs:check", "codeQuality", "classes", "test", noDistTests: true
-    quickCheck "doc:checkstyleApi", "docs:check", "codeQuality", "classes", "test", noDistTests: true
+    preCommitBuild "doc:checkstyleApi", "docs:check", "codeQuality", "classes", "test"
+    quickCheck "doc:checkstyleApi", "docs:check", "codeQuality", "classes", "test"
 
     // A full (in-process) test
     developerBuild "check"
 
     // Used by the first phase of the build pipeline
-    quickTest "test", "integTest", noDistTests: true, noDocsTests: true
+    quickTest "runtimeTests", "runtimeIntegTests"
+
+    // Used for builds to run all tests, but not necessarily on all platforms
+    fullTest "runtimeTests", "runtimeIntegTests", useIncomingDistributions: true, defaultIntegTestExecuter: "forking"
 
     // Used for builds to test the code on certain platforms
-    platformTest "test", "forkingIntegTest", noDistTests: true, noDocsTests: true, useIncomingDistributions: true
+    platformTest "runtimeTests", "runtimeIntegTests", useIncomingDistributions: true, defaultIntegTestExecuter: "forking", testAllPlatforms: true
 
     // Tests using the daemon mode
-    daemonTest "daemonIntegTest", noDistTests: true, noDocsTests: true, useIncomingDistributions: true
+    daemonTest "runtimeIntegTests", useIncomingDistributions: true, defaultIntegTestExecuter: "daemon"
 
     // Run the integration tests using the parallel executer
-    parallelTest "parallelIntegTest", noDistTests: true, noDocsTests: true, useIncomingDistributions: true
+    parallelTest "runtimeIntegTests", useIncomingDistributions: true, defaultIntegTestExecuter: "parallel"
 
     // Run the performance tests
     performanceTest "performance:integTest", useIncomingDistributions: true
@@ -53,7 +56,7 @@ buildTypes {
     localPerformanceTest "performance:integTest"
 
     // Used for cross version tests on CI
-    crossVersionTest "forkingIntegTest", crossVersionTestsOnly: "", testAllVersions: "", noDistTests: true, useIncomingDistributions: true
+    crossVersionTest "runtimeIntegTests", crossVersionTestsOnly: "", testAllVersions: "", useIncomingDistributions: true, defaultIntegTestExecuter: "forking"
 
     // Used to build production distros and smoke test them
     packageBuild "verifyIsProductionBuildEnvironment", "clean", "buildDists", "distributions:integTest"
@@ -87,7 +90,7 @@ ext {
     pluginProjects = [
         'plugins', 'codeQuality', 'jetty', 'antlr', 'wrapper', 'osgi', 'maven',
         'ide', 'announce', 'scala', 'sonar', 'signing', 'cpp', 'ear', 'javascript', 'buildComparison',
-        'diagnostics', 'reporting', 'publish', 'ivy'
+        'diagnostics', 'reporting', 'publish', 'ivy', 'jacoco', 'buildSetup', 'languageJvm', 'languageBase', 'buildSetup'
     ].collect {
         project(it)
     }
@@ -102,6 +105,7 @@ apply from: "gradle/idea.gradle"
 apply from: "gradle/eclipse.gradle"
 apply from: "gradle/classycle.gradle"
 apply from: "gradle/noDependencyResolutionDuringConfiguration.gradle"
+apply from: "gradle/testGroupings.gradle"
 
 allprojects {
     group = 'org.gradle'
@@ -193,4 +197,5 @@ task installAll(type: Install) {
     installDirPropertyName = 'gradle_installPath'
 }
 
+
 apply from: "gradle/intTestImage.gradle"
\ No newline at end of file
diff --git a/buildSrc/build.gradle b/buildSrc/build.gradle
index 6973939..3047a80 100644
--- a/buildSrc/build.gradle
+++ b/buildSrc/build.gradle
@@ -29,7 +29,7 @@ dependencies {
     compile gradleApi()
     compile 'com.google.guava:guava:11.0.2 at jar'
     compile 'commons-lang:commons-lang:2.6 at jar'
-    groovy localGroovy()
+    compile localGroovy()
     testCompile 'junit:junit:4.11 at jar'
     testCompile 'org.spockframework:spock-core:0.7-groovy-1.8 at jar', 'cglib:cglib-nodep:2.2', 'org.objenesis:objenesis:1.2'
 
diff --git a/buildSrc/src/main/groovy/org/gradle/build/BuildTypes.groovy b/buildSrc/src/main/groovy/org/gradle/build/BuildTypes.groovy
index 4490268..e582417 100644
--- a/buildSrc/src/main/groovy/org/gradle/build/BuildTypes.groovy
+++ b/buildSrc/src/main/groovy/org/gradle/build/BuildTypes.groovy
@@ -64,7 +64,7 @@ class BuildTypes {
                     if (!project.hasProperty(k)) {
                         project.ext."$k" = null
                     }
-                    project.properties."$k" = v
+                    project."$k" = v
                 }
             }
 
diff --git a/buildSrc/src/main/groovy/org/gradle/build/JarJarJar.groovy b/buildSrc/src/main/groovy/org/gradle/build/JarJarJar.groovy
index 0af4c56..9b9e789 100644
--- a/buildSrc/src/main/groovy/org/gradle/build/JarJarJar.groovy
+++ b/buildSrc/src/main/groovy/org/gradle/build/JarJarJar.groovy
@@ -17,14 +17,12 @@
 package org.gradle.build
 
 import org.gradle.api.tasks.bundling.Jar
-import org.gradle.api.*
-import org.gradle.api.file.*
-import org.gradle.api.tasks.*
 import com.tonicsystems.jarjar.Main as JarJarMain
+import org.gradle.api.tasks.Input
 
 /*
 * a Jar task that performs JarJar repackaging after archive is created
-* */
+ */
 class JarJarJar extends Jar {
     @Input def rules = [:]
     @Input def keeps = []
diff --git a/buildSrc/src/main/groovy/org/gradle/build/docs/SampleElementValidator.groovy b/buildSrc/src/main/groovy/org/gradle/build/docs/SampleElementValidator.groovy
index ca0e38c..6a917b6 100644
--- a/buildSrc/src/main/groovy/org/gradle/build/docs/SampleElementValidator.groovy
+++ b/buildSrc/src/main/groovy/org/gradle/build/docs/SampleElementValidator.groovy
@@ -19,8 +19,6 @@ import org.w3c.dom.Element
 
 /**
  * Validates sample element - looks for missing attributes.
- *
- * @author Tomek Kaczanowski
  */
 public class SampleElementValidator {
 
diff --git a/buildSrc/src/main/groovy/org/gradle/build/docs/UserGuideTransformTask.groovy b/buildSrc/src/main/groovy/org/gradle/build/docs/UserGuideTransformTask.groovy
index 02dad83..bde13c9 100644
--- a/buildSrc/src/main/groovy/org/gradle/build/docs/UserGuideTransformTask.groovy
+++ b/buildSrc/src/main/groovy/org/gradle/build/docs/UserGuideTransformTask.groovy
@@ -29,7 +29,7 @@ import org.w3c.dom.Element
 import org.gradle.api.tasks.*
 
 /**
- * Transforms userguide source into docbook, replacing custom xml elements.
+ * Transforms userguide source into docbook, replacing custom XML elements.
  *
  * Takes the following as input:
  * <ul>
@@ -227,8 +227,9 @@ public class UserGuideTransformTask extends DefaultTask {
                     String args = child.'@args'
                     String outputFile = child.'@outputFile' ?: "${sampleId}.out"
                     boolean ignoreExtraLines = child.'@ignoreExtraLines' ?: false
+                    boolean ignoreLineOrder = child.'@ignoreLineOrder' ?: false
 
-                    samplesXml << { sample(id: sampleId, dir: srcDir, args: args, outputFile: outputFile, ignoreExtraLines: ignoreExtraLines) }
+                    samplesXml << { sample(id: sampleId, dir: srcDir, args: args, outputFile: outputFile, ignoreExtraLines: ignoreExtraLines, ignoreLineOrder: ignoreLineOrder) }
 
                     Element outputTitle = doc.createElement("para")
                     outputTitle.appendChild(doc.createTextNode("Output of "))
diff --git a/buildSrc/src/main/groovy/org/gradle/build/docs/dsl/docbook/JavadocConverter.java b/buildSrc/src/main/groovy/org/gradle/build/docs/dsl/docbook/JavadocConverter.java
index 5685f02..64f27b6 100644
--- a/buildSrc/src/main/groovy/org/gradle/build/docs/dsl/docbook/JavadocConverter.java
+++ b/buildSrc/src/main/groovy/org/gradle/build/docs/dsl/docbook/JavadocConverter.java
@@ -123,6 +123,7 @@ public class JavadocConverter {
         handler.add(new LinkHandler(nodes, linkConverter, classMetaData, listener));
         handler.add(new InheritDocHandler(nodes, inheritedCommentSource));
         handler.add(new ValueTagHandler(nodes, linkConverter, classMetaData, listener));
+        handler.add(new LiteralTagHandler(nodes));
         handler.add(new TableHandler(nodes, document));
         handler.add(new DlElementHandler(nodes, document));
         handler.add(new AnchorElementHandler(nodes, document, classMetaData));
@@ -665,6 +666,22 @@ public class JavadocConverter {
         }
     }
 
+    private static class LiteralTagHandler implements JavadocTagHandler {
+        private final DocBookBuilder nodes;
+
+        private LiteralTagHandler(DocBookBuilder nodes) {
+            this.nodes = nodes;
+        }
+
+        public boolean onJavadocTag(String tag, String value) {
+            if (!tag.equals("literal")) {
+                return false;
+            }
+            nodes.appendChild(value);
+            return true;
+        }
+    }
+
     private static class LinkHandler implements JavadocTagHandler {
         private final DocBookBuilder nodes;
         private final JavadocLinkConverter linkConverter;
diff --git a/buildSrc/src/main/groovy/org/gradle/build/docs/dsl/source/ExtractDslMetaDataTask.groovy b/buildSrc/src/main/groovy/org/gradle/build/docs/dsl/source/ExtractDslMetaDataTask.groovy
index 9ca6c6c..8333f67 100644
--- a/buildSrc/src/main/groovy/org/gradle/build/docs/dsl/source/ExtractDslMetaDataTask.groovy
+++ b/buildSrc/src/main/groovy/org/gradle/build/docs/dsl/source/ExtractDslMetaDataTask.groovy
@@ -40,8 +40,8 @@ import org.gradle.build.docs.DocGenerationException
 import org.gradle.api.Transformer
 
 /**
- * Extracts meta-data from the Groovy and Java source files which make up the Gradle DSL. Persists the meta-data to a file
- * for later use in generating the docbook source for the DSL, such as by {@link org.gradle.build.docs.dsl.docbook.AssembleDslDocTask}.
+ * Extracts meta-data from the Groovy and Java source files which make up the Gradle API. Persists the meta-data to a file
+ * for later use in generating documentation for the DSL, such as by {@link org.gradle.build.docs.dsl.docbook.AssembleDslDocTask}.
  */
 class ExtractDslMetaDataTask extends SourceTask {
     @OutputFile
diff --git a/buildSrc/src/main/groovy/org/gradle/build/docs/dsl/source/GenerateDefaultImportsTask.java b/buildSrc/src/main/groovy/org/gradle/build/docs/dsl/source/GenerateDefaultImportsTask.java
new file mode 100644
index 0000000..2d4a45d
--- /dev/null
+++ b/buildSrc/src/main/groovy/org/gradle/build/docs/dsl/source/GenerateDefaultImportsTask.java
@@ -0,0 +1,148 @@
+/*
+ * Copyright 2013 the original author or authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.gradle.build.docs.dsl.source;
+
+import com.google.common.collect.HashMultimap;
+import com.google.common.collect.Multimap;
+import org.gradle.api.Action;
+import org.gradle.api.DefaultTask;
+import org.gradle.api.tasks.Input;
+import org.gradle.api.tasks.InputFile;
+import org.gradle.api.tasks.OutputFile;
+import org.gradle.api.tasks.TaskAction;
+import org.gradle.build.docs.dsl.source.model.ClassMetaData;
+import org.gradle.build.docs.model.SimpleClassMetaDataRepository;
+
+import java.io.File;
+import java.io.FileWriter;
+import java.io.IOException;
+import java.io.PrintWriter;
+import java.util.*;
+
+public class GenerateDefaultImportsTask extends DefaultTask {
+    private File metaDataFile;
+    private File destFile;
+    private Set<String> excludePatterns = new HashSet<String>();
+    private Set<String> extraPackages = new HashSet<String>();
+
+    @InputFile
+    public File getMetaDataFile() {
+        return metaDataFile;
+    }
+
+    public void setMetaDataFile(File metaDataFile) {
+        this.metaDataFile = metaDataFile;
+    }
+
+    @OutputFile
+    public File getDestFile() {
+        return destFile;
+    }
+
+    public void setDestFile(File destFile) {
+        this.destFile = destFile;
+    }
+
+    @Input
+    public Set<String> getExcludedPackages() {
+        return excludePatterns;
+    }
+
+    public void setExcludedPackages(Set<String> excludedPackages) {
+        this.excludePatterns = excludedPackages;
+    }
+
+    /**
+     * Package name can end with '.**' to exclude subpackages as well.
+     */
+    public void excludePackage(String name) {
+        excludePatterns.add(name);
+    }
+
+    public Set<String> getExtraPackages() {
+        return extraPackages;
+    }
+
+    public void setExtraPackages(Set<String> extraPackages) {
+        this.extraPackages = extraPackages;
+    }
+
+    public void extraPackage(String name) {
+        extraPackages.add(name);
+    }
+
+    @TaskAction
+    public void generate() throws IOException {
+        SimpleClassMetaDataRepository<ClassMetaData> repository = new SimpleClassMetaDataRepository<ClassMetaData>();
+        repository.load(getMetaDataFile());
+
+        final Set<String> excludedPrefixes = new HashSet<String>();
+        final Set<String> excludedPackages = new HashSet<String>();
+        for (String excludePattern : excludePatterns) {
+            if (excludePattern.endsWith(".**")) {
+                String baseName = excludePattern.substring(0, excludePattern.length() - 3);
+                excludedPrefixes.add(baseName + '.');
+                excludedPackages.add(baseName);
+            } else {
+                excludedPackages.add(excludePattern);
+            }
+        }
+        final Set<String> packages = new TreeSet<String>();
+        packages.addAll(extraPackages);
+        final Multimap<String, String> simpleNames = HashMultimap.create();
+
+        repository.each(new Action<ClassMetaData>() {
+            public void execute(ClassMetaData classMetaData) {
+                if (classMetaData.getOuterClassName() != null) {
+                    // Ignore inner classes
+                    return;
+                }
+                String packageName = classMetaData.getPackageName();
+                if (excludedPackages.contains(packageName)) {
+                    return;
+                }
+                for (String excludedPrefix : excludedPrefixes) {
+                    if (packageName.startsWith(excludedPrefix)) {
+                        return;
+                    }
+                }
+                simpleNames.put(classMetaData.getSimpleName(), classMetaData.getClassName());
+                packages.add(packageName);
+            }
+        });
+
+        for (Map.Entry<String, Collection<String>> entry : simpleNames.asMap().entrySet()) {
+            if (entry.getValue().size() > 1) {
+                System.out.println(String.format("Multiple DSL types have short name '%s'", entry.getKey()));
+                for (String className : entry.getValue()) {
+                    System.out.println("    * " + className);
+                }
+            }
+        }
+
+        final PrintWriter writer = new PrintWriter(new FileWriter(getDestFile()));
+        try {
+            for (String packageName : packages) {
+                writer.print("import ");
+                writer.print(packageName);
+                writer.println(".*");
+            }
+        } finally {
+            writer.close();
+        }
+    }
+}
diff --git a/buildSrc/src/main/groovy/org/gradle/build/docs/model/SimpleClassMetaDataRepository.java b/buildSrc/src/main/groovy/org/gradle/build/docs/model/SimpleClassMetaDataRepository.java
index f5ec24e..3811902 100644
--- a/buildSrc/src/main/groovy/org/gradle/build/docs/model/SimpleClassMetaDataRepository.java
+++ b/buildSrc/src/main/groovy/org/gradle/build/docs/model/SimpleClassMetaDataRepository.java
@@ -16,12 +16,12 @@
 package org.gradle.build.docs.model;
 
 import groovy.lang.Closure;
+import org.gradle.api.Action;
 import org.gradle.api.GradleException;
 import org.gradle.api.UnknownDomainObjectException;
 
 import java.io.*;
-import java.util.HashMap;
-import java.util.Map;
+import java.util.*;
 
 public class SimpleClassMetaDataRepository<T extends Attachable<T>> implements ClassMetaDataRepository<T> {
     private final Map<String, T> classes = new HashMap<String, T>();
@@ -82,4 +82,10 @@ public class SimpleClassMetaDataRepository<T extends Attachable<T>> implements C
             cl.call(new Object[]{entry.getKey(), entry.getValue()});
         }
     }
+
+    public void each(Action<? super T> action) {
+        for (T t : classes.values()) {
+            action.execute(t);
+        }
+    }
 }
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 9749b9d..e44d297 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
@@ -140,6 +140,16 @@ line 2</para>'''
         format(result.docbook) == '''<para>This is <literal>code</literal>. So is <literal>this</literal>.</para>'''
     }
 
+    def convertsLiteralTagsToText() {
+        _ * classMetaData.rawCommentText >> '{@literal <b>markup</b> {@ignore}}'
+
+        when:
+        def result = parser.parse(classMetaData, listener)
+
+        then:
+        format(result.docbook) == '''<para><b>markup</b> {@ignore}</para>'''
+    }
+
     def doesNotInterpretContentsOfCodeTagAsHtml() {
         _ * classMetaData.rawCommentText >> '{@code List<String> && a < 9} <code>&</code>'
 
diff --git a/buildSrc/src/test/groovy/org/gradle/build/docs/model/SimpleClassMetaDataRepositoryTest.groovy b/buildSrc/src/test/groovy/org/gradle/build/docs/model/SimpleClassMetaDataRepositoryTest.groovy
index 23e867b..1ce3f08 100644
--- a/buildSrc/src/test/groovy/org/gradle/build/docs/model/SimpleClassMetaDataRepositoryTest.groovy
+++ b/buildSrc/src/test/groovy/org/gradle/build/docs/model/SimpleClassMetaDataRepositoryTest.groovy
@@ -15,10 +15,14 @@
  */
 package org.gradle.build.docs.model
 
-import spock.lang.Specification
+import org.gradle.api.Action
 import org.gradle.api.UnknownDomainObjectException
+import org.junit.Rule
+import org.junit.rules.TemporaryFolder
+import spock.lang.Specification
 
 class SimpleClassMetaDataRepositoryTest extends Specification {
+    @Rule TemporaryFolder tmpDir
     final SimpleClassMetaDataRepository<TestDomainObject> repository = new SimpleClassMetaDataRepository<TestDomainObject>()
 
     def canAddMetaData() {
@@ -46,7 +50,7 @@ class SimpleClassMetaDataRepositoryTest extends Specification {
         e.message == 'No meta-data is available for class \'unknown\'.'
     }
 
-    def canIterateOverClasses() {
+    def canIterateOverClassesUsingClosure() {
         TestDomainObject value1 = new TestDomainObject('a')
         TestDomainObject value2 = new TestDomainObject('a')
         repository.put('class1', value1)
@@ -62,9 +66,25 @@ class SimpleClassMetaDataRepositoryTest extends Specification {
         0 * cl._
     }
 
+    def canIterateOverClassesUsingAction() {
+        TestDomainObject value1 = new TestDomainObject('a')
+        TestDomainObject value2 = new TestDomainObject('a')
+        repository.put('class1', value1)
+        repository.put('class2', value2)
+        Action action = Mock()
+
+        when:
+        repository.each(action)
+
+        then:
+        1 * action.execute(value1)
+        1 * action.execute(value2)
+        0 * action._
+    }
+
     def canPersistMetaData() {
         TestDomainObject value = new TestDomainObject('a')
-        File file = File.createTempFile("test-repository", "bin")
+        File file = tmpDir.newFile()
         repository.put('class', value)
 
         when:
diff --git a/config/checkstyle/checkstyle.xml b/config/checkstyle/checkstyle.xml
index 6b0b710..742953d 100644
--- a/config/checkstyle/checkstyle.xml
+++ b/config/checkstyle/checkstyle.xml
@@ -111,6 +111,9 @@
     <module name="RegexpSingleline">
         <property name="format" value="Created with IntelliJ IDEA"/>
     </module>
+    <module name="RegexpSingleline">
+        <property name="format" value="^\s*\*\s*@author"/>
+    </module>
 
     <!-- allows suppressing using the //CHECKSTYLE:ON //CHECKSTYLE:OFF -->
     <module name="SuppressionCommentFilter"/>
diff --git a/gradle/buildReceipt.gradle b/gradle/buildReceipt.gradle
index 3139797..eb0209e 100644
--- a/gradle/buildReceipt.gradle
+++ b/gradle/buildReceipt.gradle
@@ -118,6 +118,7 @@ task createBuildReceipt(dependsOn: determineCommitId) {
                 isSnapshot: isSnapshot,
                 rcNumber: rcNumber == null ? "" : rcNumber,
                 buildTimestamp: buildTimestamp,
+                buildNumber: System.properties["build.number"] ?: 'none',
                 username: System.properties["user.name"],
                 hostname: hostName,
                 javaVersion: System.properties["java.version"],
diff --git a/gradle/dependencies.gradle b/gradle/dependencies.gradle
index 0face90..0f8f692 100755
--- a/gradle/dependencies.gradle
+++ b/gradle/dependencies.gradle
@@ -21,8 +21,8 @@ ext {
 
 versions.commons_io = 'commons-io:commons-io:1.4'
 
-libraries.ant = dependencies.module('org.apache.ant:ant:1.8.4') {
-    dependency 'org.apache.ant:ant-launcher:1.8.4 at jar'
+libraries.ant = dependencies.module('org.apache.ant:ant:1.9.2') {
+    dependency 'org.apache.ant:ant-launcher:1.9.2 at jar'
 }
 
 libraries.asm =  'org.ow2.asm:asm-all:4.0 at jar'
@@ -76,7 +76,7 @@ libraries.maven_ant_tasks = dependencies.module("org.apache.maven:maven-ant-task
 }
 
 libraries += [
-        ant_antlr: 'org.apache.ant:ant-antlr:1.8.4 at jar',
+        ant_antlr: 'org.apache.ant:ant-antlr:1.9.2 at jar',
         antlr: 'antlr:antlr:2.7.7 at jar',
         dom4j: 'dom4j:dom4j:1.6.1 at jar',
         guava: 'com.google.guava:guava:11.0.2 at jar',
diff --git a/gradle/groovyProject.gradle b/gradle/groovyProject.gradle
index f69306b..6b7b3dc 100644
--- a/gradle/groovyProject.gradle
+++ b/gradle/groovyProject.gradle
@@ -8,7 +8,6 @@ sourceCompatibility = 1.5
 targetCompatibility = 1.5
 
 ext {
-    hasGroovySource = sourceSets.main.groovy.srcDirs.any { it.exists() }
     compileTasks = tasks.matching { it instanceof Compile || it instanceof GroovyCompile }
     testTasks = tasks.withType(Test)
     generatedResourcesDir = file("$buildDir/generated-resources/main")
@@ -20,11 +19,6 @@ dependencies {
     testCompile libraries.junit, libraries.jmock, libraries.spock
 }
 
-if (!hasGroovySource) {
-    // Remove Groovy configuration from compile classpath
-    configurations.compile.extendsFrom = []
-}
-
 // Extracted as it's also used by buildSrc
 apply from: "$rootDir/gradle/compile.gradle"
 
@@ -97,4 +91,16 @@ class ClasspathManifest extends DefaultTask {
         manifestFile.withOutputStream { properties.save(it, 'module definition') }
     }
 }
-ext.ClasspathManifest = ClasspathManifest
\ No newline at end of file
+ext.ClasspathManifest = ClasspathManifest
+
+// This is here mostly for the TravisCI build.
+// It advertises as an ANSI capable terminal but isn't, so we run with -q there to make
+// the output legible, but we still want test result info
+tasks.withType(Test) {
+    testLogging {
+        quiet {
+            events "failed"
+            exceptionFormat "full"
+        }
+    }
+}
\ No newline at end of file
diff --git a/gradle/integTest.gradle b/gradle/integTest.gradle
index d5b36ca..6c8944e 100644
--- a/gradle/integTest.gradle
+++ b/gradle/integTest.gradle
@@ -29,7 +29,7 @@ integTestTasks.all { Test task ->
     testSrcDirs = []
     jvmArgs '-Xmx512m', '-XX:MaxPermSize=256m', '-XX:+HeapDumpOnOutOfMemoryError'
 
-    testResultsDir = file("${project.testResultsDir}/$name")
+    reports.junitXml.destination = file("${project.testResultsDir}/$name")
 
     systemProperties['org.gradle.integtest.versions'] = project.hasProperty("testAllVersions") ? 'all' : 'latest'
     if (project.hasProperty('crossVersionTestsOnly')) {
@@ -37,9 +37,11 @@ integTestTasks.all { Test task ->
         forkEvery = 1
     }
 
+    systemProperties['org.gradle.integtest.cpp.toolChains'] = project.hasProperty("testAllPlatforms") ? 'all' : 'default';
+
     dependsOn project.task("configure${task.name.capitalize()}") << {
         configure(task) {
-            testReportDir = file("${project.reporting.baseDir}/$name")
+            reports.html.destination = file("${project.reporting.baseDir}/$name")
             systemProperties['integTest.gradleHomeDir'] = rootProject.intTestImage.destinationDir.absolutePath
             systemProperties['integTest.gradleUserHomeDir'] = rootProject.file('intTestHomeDir').absolutePath
             systemProperties['integTest.libsRepo'] = rootProject.file('build/repo')
@@ -63,7 +65,7 @@ check.dependsOn(integTest)
 
 ['embedded', 'forking', 'daemon', 'embeddedDaemon', 'parallel'].each { mode ->
     def taskName = "${mode}IntegTest"
-    tasks.add(taskName, Test).configure {
+    tasks.create(taskName, Test).configure {
         systemProperties['org.gradle.integtest.executer'] = mode
     }
 }
diff --git a/gradle/testGroupings.gradle b/gradle/testGroupings.gradle
new file mode 100644
index 0000000..52945e1
--- /dev/null
+++ b/gradle/testGroupings.gradle
@@ -0,0 +1,13 @@
+// only the projects that contribute runtime code
+def runtimeProjects = subprojects - [':docs', ':distributions', ':performance'].collect { project(it) }
+
+def runtimeProjectTasks = { String taskName ->
+    runtimeProjects.collect { it.tasks.findByPath(taskName) }.findAll { it != null }
+}
+task runtimeTests {
+    dependsOn { runtimeProjectTasks("test") }
+}
+
+task runtimeIntegTests {
+    dependsOn { runtimeProjectTasks("integTest") }
+}
\ No newline at end of file
diff --git a/gradle/versioning.gradle b/gradle/versioning.gradle
index 7b715eb..1d93b09 100644
--- a/gradle/versioning.gradle
+++ b/gradle/versioning.gradle
@@ -1,6 +1,10 @@
 def timestampFormat = new java.text.SimpleDateFormat('yyyyMMddHHmmssZ')
 timestampFormat.timeZone = TimeZone.getTimeZone("UTC")
 
+if (buildTypes.isActive("promotionBuild")) {
+    logger.lifecycle "Invocation tasks: $gradle.startParameter.taskNames\nInvocation properties: $gradle.startParameter.projectProperties"
+}
+
 if (incomingDistributionsBuildReceipt) {
     ext.versionBase = incomingDistributionsBuildReceipt.versionBase
     ext.buildTimestamp = incomingDistributionsBuildReceipt.buildTimestamp
diff --git a/gradle/wrapper.gradle b/gradle/wrapper.gradle
index 5fd0747..dc447f5 100644
--- a/gradle/wrapper.gradle
+++ b/gradle/wrapper.gradle
@@ -15,19 +15,27 @@
  */
 
 def wrapperUpdateTask = { name, label ->
-    task "${name}Wrapper"(type: Wrapper) {
-        group = "wrapper"
-        doFirst {
-            def version = new groovy.json.JsonSlurper().parseText(new URL("http://services.gradle.org/versions/$label").text)
-            if (version.empty) {
-                throw new GradleException("Cannot update wrapper to '${label}' version as there is currently no version of that label")
+    def wrapperTaskName = "${name}Wrapper"
+    def configureWrapperTaskName = "configure${wrapperTaskName.capitalize()}"
+
+    task "$configureWrapperTaskName" {
+        doLast {
+            configure(tasks."$wrapperTaskName") {
+                def version = new groovy.json.JsonSlurper().parseText(new URL("http://services.gradle.org/versions/$label").text)
+                if (version.empty) {
+                    throw new GradleException("Cannot update wrapper to '${label}' version as there is currently no version of that label")
+                }
+                println "updating wrapper to $label version: $version.version (downloadUrl: $version.downloadUrl)"
+                distributionUrl version.downloadUrl
             }
-            println "updating wrapper to $label version: $version.version (downloadUrl: $version.downloadUrl)"
-            distributionUrl version.downloadUrl
         }
+    }
+
+    task "${wrapperTaskName}"(type: Wrapper, dependsOn: configureWrapperTaskName) {
+        group = "wrapper"
+		def jvmOpts = "-Xmx1024m -XX:MaxPermSize=256m -Dfile.encoding=UTF-8"
+        inputs.property("jvmOpts", jvmOpts)        
         doLast {
-            def jvmOpts = "-Xmx1024m -XX:MaxPermSize=256m -Dfile.encoding=UTF-8"
-            inputs.property("jvmOpts", jvmOpts)
             def optsEnvVar = "DEFAULT_JVM_OPTS"
             scriptFile.write scriptFile.text.replace("$optsEnvVar=\"\"", "$optsEnvVar=\"$jvmOpts\"")
             batchScript.write batchScript.text.replace("set $optsEnvVar=", "set $optsEnvVar=$jvmOpts")
diff --git a/gradle/wrapper/gradle-wrapper.properties b/gradle/wrapper/gradle-wrapper.properties
index 8e1362d..95f5809 100644
--- a/gradle/wrapper/gradle-wrapper.properties
+++ b/gradle/wrapper/gradle-wrapper.properties
@@ -1,6 +1,6 @@
-#Mon Mar 11 12:15:50 GMT 2013
+#Tue Aug 06 12:46:48 BST 2013
 distributionBase=GRADLE_USER_HOME
 distributionPath=wrapper/dists
 zipStoreBase=GRADLE_USER_HOME
 zipStorePath=wrapper/dists
-distributionUrl=http\://services.gradle.org/distributions/gradle-1.5-rc-1-bin.zip
+distributionUrl=http\://services.gradle.org/distributions/gradle-1.7-bin.zip
diff --git a/settings.gradle b/settings.gradle
index 801a90d..2b7976f 100644
--- a/settings.gradle
+++ b/settings.gradle
@@ -22,6 +22,7 @@ include 'wrapper'
 include 'cli'
 include 'launcher'
 include 'messaging'
+include 'resources'
 include 'plugins'
 include 'scala'
 include 'ide'
@@ -50,6 +51,10 @@ include 'reporting'
 include 'diagnostics'
 include 'publish'
 include 'ivy'
+include 'jacoco'
+include 'buildSetup'
+include 'languageBase'
+include 'languageJvm'
 
 rootProject.name = 'gradle'
 rootProject.children.each {project ->
diff --git a/subprojects/announce/announce.gradle b/subprojects/announce/announce.gradle
index 68a1e47..6787365 100644
--- a/subprojects/announce/announce.gradle
+++ b/subprojects/announce/announce.gradle
@@ -14,8 +14,7 @@
  * limitations under the License.
  */
 dependencies {
-    groovy libraries.groovy
-
+    compile libraries.groovy
     compile libraries.slf4j_api
     compile project(':core')
     integTestRuntime project(':plugins')
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 87d879d..d9b630e 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
@@ -18,16 +18,13 @@ package org.gradle.api.plugins.announce
 
 import org.gradle.api.Plugin
 import org.gradle.api.Project
-import org.gradle.api.internal.project.ProjectInternal
 
 /**
  * This plugin allows to send announce messages to Twitter.
- *
- * @author hackergarten
  */
 class AnnouncePlugin implements Plugin<Project> {
     void apply(Project project) {
-        project.extensions.add("announce", new AnnouncePluginExtension((ProjectInternal)project))
+        project.extensions.create("announce", AnnouncePluginExtension.class, project)
     }
 }
 
diff --git a/subprojects/announce/src/main/groovy/org/gradle/api/plugins/announce/AnnouncePluginExtension.groovy b/subprojects/announce/src/main/groovy/org/gradle/api/plugins/announce/AnnouncePluginExtension.groovy
index ab6f62c..55ffd22 100644
--- a/subprojects/announce/src/main/groovy/org/gradle/api/plugins/announce/AnnouncePluginExtension.groovy
+++ b/subprojects/announce/src/main/groovy/org/gradle/api/plugins/announce/AnnouncePluginExtension.groovy
@@ -37,14 +37,14 @@ class AnnouncePluginExtension {
      */
     String password
 
-    Announcer local
-
+    private final onDemandLocalAnnouncer
     private final Project project
     AnnouncerFactory announcerFactory
 
     AnnouncePluginExtension(ProjectInternal project) {
         this.project = project
         this.announcerFactory = new DefaultAnnouncerFactory(this, project, new DefaultIconProvider(project.services.get(GradleDistributionLocator)))
+        this.onDemandLocalAnnouncer = new LocalAnnouncer(this)
     }
 
     /**
@@ -53,14 +53,14 @@ class AnnouncePluginExtension {
      * @return The announcer.
      */
     Announcer getLocal() {
-        return new Announcer() {
-            void send(String title, String message) {
-                if (local == null) {
-                    local = announcerFactory.createAnnouncer("local")
-                }
-                local.send(title, message)
-            }
-        }
+        return onDemandLocalAnnouncer
+    }
+
+    /**
+     * Sets the {@link Announcer} that should be used to send announcements to the local desktop.
+     */
+    void setLocal(Announcer localAnnouncer) {
+        onDemandLocalAnnouncer.local = localAnnouncer
     }
 
     /**
@@ -76,4 +76,20 @@ class AnnouncePluginExtension {
             logger.warn("Failed to send message '$msg' to '$type'", e)
         }
     }
+
+    private static class LocalAnnouncer implements Announcer {
+        AnnouncePluginExtension extension
+        Announcer local
+
+        LocalAnnouncer(AnnouncePluginExtension extension) {
+            this.extension = extension
+        }
+
+        void send(String title, String message) {
+            if (local == null) {
+                local = extension.getAnnouncerFactory().createAnnouncer("local")
+            }
+            local.send(title, 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
index edd5270..b11870f 100644
--- 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
@@ -17,9 +17,6 @@ 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/DefaultAnnouncerFactory.groovy b/subprojects/announce/src/main/groovy/org/gradle/api/plugins/announce/internal/DefaultAnnouncerFactory.groovy
index 8c5b687..d78f09a 100755
--- 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
@@ -22,9 +22,6 @@ import org.gradle.api.plugins.announce.AnnouncePluginExtension
 import org.gradle.api.plugins.announce.Announcer
 import org.gradle.internal.os.OperatingSystem
 
-/**
- * @author Hans Dockter
- */
 class DefaultAnnouncerFactory implements AnnouncerFactory {
     private final AnnouncePluginExtension announcePluginConvention
     private final IconProvider iconProvider
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 f74a00f..ebcc3d4 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
@@ -23,10 +23,7 @@ import org.gradle.internal.os.OperatingSystem
 
 /**
  * This class wraps the Ubuntu Notify Send functionality.
- *
- * @author Hackergarten
  */
-
 class NotifySend implements Announcer {
     private final IconProvider iconProvider
     private final ProcessOperations processOperations
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 a697f15..70e0664 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
@@ -14,17 +14,15 @@
  * limitations under the License.
  */
 
-package org.gradle.api.plugins.announce.internal;
+package org.gradle.api.plugins.announce.internal
 
+import org.gradle.api.plugins.announce.Announcer
+import org.slf4j.Logger
 import org.slf4j.LoggerFactory
 import sun.misc.BASE64Encoder
-import org.slf4j.Logger
-import org.gradle.api.plugins.announce.Announcer
 
 /**
  * This class allows to send announce messages to Twitter.
- *
- * @author Hackergarten
  */
 class Twitter implements Announcer {
     private static final Logger logger = LoggerFactory.getLogger(Twitter)
diff --git a/subprojects/announce/src/test/groovy/org/gradle/api/plugins/announce/AnnouncePluginExtensionTest.groovy b/subprojects/announce/src/test/groovy/org/gradle/api/plugins/announce/AnnouncePluginExtensionTest.groovy
index a2279f4..0d7b22f 100644
--- a/subprojects/announce/src/test/groovy/org/gradle/api/plugins/announce/AnnouncePluginExtensionTest.groovy
+++ b/subprojects/announce/src/test/groovy/org/gradle/api/plugins/announce/AnnouncePluginExtensionTest.groovy
@@ -17,16 +17,12 @@ package org.gradle.api.plugins.announce
 
 import org.gradle.api.internal.project.ProjectInternal
 import org.gradle.api.plugins.announce.internal.AnnouncerFactory
+import org.gradle.util.TestUtil
 import spock.lang.Specification
-import org.gradle.util.HelperUtil
-
-/**
- * @author Hans Dockter
- */
 
 class AnnouncePluginExtensionTest extends Specification {
     final AnnouncerFactory announcerFactory = Mock()
-    final ProjectInternal project = HelperUtil.createRootProject()
+    final ProjectInternal project = TestUtil.createRootProject()
     final AnnouncePluginExtension announcePluginConvention = new AnnouncePluginExtension(project)
 
     def setup() {
diff --git a/subprojects/announce/src/test/groovy/org/gradle/api/plugins/announce/AnnouncePluginTest.groovy b/subprojects/announce/src/test/groovy/org/gradle/api/plugins/announce/AnnouncePluginTest.groovy
index 939c63b..b03bbe4 100644
--- a/subprojects/announce/src/test/groovy/org/gradle/api/plugins/announce/AnnouncePluginTest.groovy
+++ b/subprojects/announce/src/test/groovy/org/gradle/api/plugins/announce/AnnouncePluginTest.groovy
@@ -15,16 +15,13 @@
  */
 package org.gradle.api.plugins.announce
 
-import spock.lang.Specification
-import org.gradle.util.HelperUtil
 import org.gradle.api.Project
+import org.gradle.util.TestUtil
+import spock.lang.Specification
 
-/**
- * @author Hans Dockter
- */
 class AnnouncePluginTest extends Specification {
     AnnouncePlugin announcePlugin = new AnnouncePlugin()
-    Project project = HelperUtil.createRootProject()
+    Project project = TestUtil.createRootProject()
 
     def addExtensionObject() {
         when:
diff --git a/subprojects/announce/src/test/groovy/org/gradle/api/plugins/announce/BuildAnnouncementsPluginTest.groovy b/subprojects/announce/src/test/groovy/org/gradle/api/plugins/announce/BuildAnnouncementsPluginTest.groovy
index 3604998..178ea23 100644
--- a/subprojects/announce/src/test/groovy/org/gradle/api/plugins/announce/BuildAnnouncementsPluginTest.groovy
+++ b/subprojects/announce/src/test/groovy/org/gradle/api/plugins/announce/BuildAnnouncementsPluginTest.groovy
@@ -16,11 +16,11 @@
 package org.gradle.api.plugins.announce
 
 import spock.lang.Specification
-import org.gradle.util.HelperUtil
+import org.gradle.util.TestUtil
 import org.gradle.api.Project
 
 class BuildAnnouncementsPluginTest extends Specification {
-    final Project project = HelperUtil.createRootProject()
+    final Project project = TestUtil.createRootProject()
     final BuildAnnouncementsPlugin plugin = new BuildAnnouncementsPlugin()
 
     def "applies announce plugin"() {
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 48dcbe3..97289bf 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
@@ -18,14 +18,11 @@ package org.gradle.api.plugins.announce.internal
 import org.gradle.api.internal.ProcessOperations
 import org.gradle.api.plugins.announce.AnnouncePluginExtension
 import org.gradle.internal.os.OperatingSystem
-import org.gradle.util.HelperUtil
+import org.gradle.util.TestUtil
 import spock.lang.Specification
 
-/**
- * @author Hans Dockter
- */
 class DefaultAnnouncerFactoryTest extends Specification {
-    final project = HelperUtil.createRootProject()
+    final project = TestUtil.createRootProject()
     final extension = new AnnouncePluginExtension(project)
     final ProcessOperations processOperations = Mock()
     final IconProvider iconProvider = Mock()
diff --git a/subprojects/antlr/antlr.gradle b/subprojects/antlr/antlr.gradle
index 5c614f1..9a1eca4 100644
--- a/subprojects/antlr/antlr.gradle
+++ b/subprojects/antlr/antlr.gradle
@@ -14,7 +14,7 @@
  * limitations under the License.
  */
 dependencies {
-    groovy libraries.groovy
+    compile libraries.groovy
 
     compile project(':core')
     compile project(':plugins')
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 ee2a663..56dfe62 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
@@ -36,8 +36,6 @@ import static org.gradle.api.plugins.JavaPlugin.COMPILE_CONFIGURATION_NAME;
 
 /**
  * A plugin for adding Antlr support to {@link JavaPlugin java projects}.
- *
- * @author Steve Ebersole
  */
 public class AntlrPlugin implements Plugin<ProjectInternal> {
     public static final String ANTLR_CONFIGURATION_NAME = "antlr";
@@ -53,7 +51,7 @@ public class AntlrPlugin implements Plugin<ProjectInternal> {
 
         // set up a configuration named 'antlr' for the user to specify the antlr libs to use in case
         // they want a specific version etc.
-        Configuration antlrConfiguration = project.getConfigurations().add(ANTLR_CONFIGURATION_NAME).setVisible(false)
+        Configuration antlrConfiguration = project.getConfigurations().create(ANTLR_CONFIGURATION_NAME).setVisible(false)
                 .setTransitive(false).setDescription("The Antlr libraries to be used for this project.");
         project.getConfigurations().getByName(COMPILE_CONFIGURATION_NAME).extendsFrom(antlrConfiguration);
 
@@ -73,7 +71,7 @@ public class AntlrPlugin implements Plugin<ProjectInternal> {
                         // 2) create an AntlrTask for this sourceSet following the gradle
                         //    naming conventions via call to sourceSet.getTaskName()
                         final String taskName = sourceSet.getTaskName("generate", "GrammarSource");
-                        AntlrTask antlrTask = project.getTasks().add(taskName, AntlrTask.class);
+                        AntlrTask antlrTask = project.getTasks().create(taskName, AntlrTask.class);
                         antlrTask.setDescription(String.format("Processes the %s Antlr grammars.",
                                 sourceSet.getName()));
 
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 2907b23..cd71f72 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
@@ -24,8 +24,6 @@ import org.gradle.api.file.SourceDirectorySet;
  * injecting a virtual directory named 'antlr' into the project's various {@link org.gradle.api.tasks.SourceSet source
  * sets}.  Its implementation gets pushed onto the {@link org.gradle.api.internal.DynamicObjectAware} portion of the
  * source set under the name 'antlr'.
- *
- * @author Steve Ebersole
  */
 public interface AntlrSourceVirtualDirectory {
     public static final String NAME = "antlr";
diff --git a/subprojects/antlr/src/main/groovy/org/gradle/api/plugins/antlr/AntlrTask.java b/subprojects/antlr/src/main/groovy/org/gradle/api/plugins/antlr/AntlrTask.java
index 452f97a..44a2696 100644
--- a/subprojects/antlr/src/main/groovy/org/gradle/api/plugins/antlr/AntlrTask.java
+++ b/subprojects/antlr/src/main/groovy/org/gradle/api/plugins/antlr/AntlrTask.java
@@ -16,24 +16,24 @@
 
 package org.gradle.api.plugins.antlr;
 
-import java.io.File;
-import java.util.List;
-
 import org.apache.tools.ant.taskdefs.optional.ANTLR;
 import org.apache.tools.ant.types.Path;
 import org.gradle.api.file.FileCollection;
 import org.gradle.api.plugins.antlr.internal.GenerationPlan;
+import org.gradle.api.plugins.antlr.internal.GenerationPlanBuilder;
+import org.gradle.api.plugins.antlr.internal.MetadataExtracter;
+import org.gradle.api.plugins.antlr.internal.XRef;
 import org.gradle.api.tasks.InputFiles;
 import org.gradle.api.tasks.OutputDirectory;
 import org.gradle.api.tasks.SourceTask;
 import org.gradle.api.tasks.TaskAction;
-import org.gradle.api.plugins.antlr.internal.MetadataExtracter;
-import org.gradle.api.plugins.antlr.internal.XRef;
-import org.gradle.api.plugins.antlr.internal.GenerationPlanBuilder;
 import org.gradle.util.GFileUtils;
 import org.slf4j.Logger;
 import org.slf4j.LoggerFactory;
 
+import java.io.File;
+import java.util.List;
+
 /**
  * <p>Generates parsers from Antlr grammars.</p>
  *
@@ -42,8 +42,6 @@ import org.slf4j.LoggerFactory;
  * classpath it uses to perform generation execution.  This <b>should</b> really only require the antlr jar.  In {@link
  * AntlrPlugin} usage, this would happen simply by adding your antlr jar into the 'antlr' dependency configuration
  * created and exposed by the {@link AntlrPlugin} itself.</p>
- *
- * @author Steve Ebersole
  */
 public class AntlrTask extends SourceTask {
     private static final Logger LOGGER = LoggerFactory.getLogger(AntlrTask.class);
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 27dced5..006320d 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
@@ -24,8 +24,6 @@ import org.gradle.util.ConfigureUtil;
 
 /**
  * The implementation of the {@link org.gradle.api.plugins.antlr.AntlrSourceVirtualDirectory} contract.
- *
- * @author Steve Ebersole
  */
 public class AntlrSourceVirtualDirectoryImpl implements AntlrSourceVirtualDirectory {
     private final SourceDirectorySet antlr;
diff --git a/subprojects/antlr/src/main/groovy/org/gradle/api/plugins/antlr/internal/GenerationPlan.java b/subprojects/antlr/src/main/groovy/org/gradle/api/plugins/antlr/internal/GenerationPlan.java
index a6b0db7..97d1247 100644
--- a/subprojects/antlr/src/main/groovy/org/gradle/api/plugins/antlr/internal/GenerationPlan.java
+++ b/subprojects/antlr/src/main/groovy/org/gradle/api/plugins/antlr/internal/GenerationPlan.java
@@ -19,8 +19,6 @@ import java.io.File;
 
 /**
  * Models information relevant to generation of a particular Antlr grammar file.
- *
- * @author Steve Ebersole
  */
 public class GenerationPlan {
     private final File source;
diff --git a/subprojects/antlr/src/main/groovy/org/gradle/api/plugins/antlr/internal/GenerationPlanBuilder.java b/subprojects/antlr/src/main/groovy/org/gradle/api/plugins/antlr/internal/GenerationPlanBuilder.java
index 0cad3f1..2d82713 100644
--- a/subprojects/antlr/src/main/groovy/org/gradle/api/plugins/antlr/internal/GenerationPlanBuilder.java
+++ b/subprojects/antlr/src/main/groovy/org/gradle/api/plugins/antlr/internal/GenerationPlanBuilder.java
@@ -15,21 +15,19 @@
  */
 package org.gradle.api.plugins.antlr.internal;
 
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
 import java.io.File;
+import java.util.ArrayList;
+import java.util.Iterator;
 import java.util.LinkedHashMap;
 import java.util.List;
-import java.util.Iterator;
-import java.util.ArrayList;
-
-import org.slf4j.Logger;
-import org.slf4j.LoggerFactory;
 
 /**
  * Builder for the properly order list of {@link GenerationPlan generation plans}.
  *
  * <p>IMPL NOTE : Uses recursive calls to achieve ordering.</p>
- *
- * @author Steve Ebersole
  */
 public class GenerationPlanBuilder {
     private static final Logger LOGGER = LoggerFactory.getLogger(GenerationPlanBuilder.class);
diff --git a/subprojects/antlr/src/main/groovy/org/gradle/api/plugins/antlr/internal/GrammarDelegate.java b/subprojects/antlr/src/main/groovy/org/gradle/api/plugins/antlr/internal/GrammarDelegate.java
index 255fd29..497eb88 100644
--- a/subprojects/antlr/src/main/groovy/org/gradle/api/plugins/antlr/internal/GrammarDelegate.java
+++ b/subprojects/antlr/src/main/groovy/org/gradle/api/plugins/antlr/internal/GrammarDelegate.java
@@ -15,19 +15,17 @@
  */
 package org.gradle.api.plugins.antlr.internal;
 
+import antlr.collections.impl.IndexedVector;
+import antlr.preprocessor.GrammarFile;
+
 import java.lang.reflect.Method;
-import java.util.Enumeration;
 import java.util.ArrayList;
+import java.util.Enumeration;
 import java.util.List;
 
-import antlr.collections.impl.IndexedVector;
-import antlr.preprocessor.GrammarFile;
-
 /**
  * Antlr defines its {@link antlr.preprocessor.Grammar} class as package-protected for some unfortunate reason. So this class acts as a delegate to the Antlr {@link antlr.preprocessor.Grammar} class,
  * hiding all the ugly necessary reflection code.
- *
- * @author Steve Ebersole
  */
 public class GrammarDelegate {
     public static List<GrammarDelegate> extractGrammarDelegates(GrammarFile antlrGrammarFile) {
diff --git a/subprojects/antlr/src/main/groovy/org/gradle/api/plugins/antlr/internal/GrammarFileMetadata.java b/subprojects/antlr/src/main/groovy/org/gradle/api/plugins/antlr/internal/GrammarFileMetadata.java
index 0ef041f..7d3daa5 100644
--- a/subprojects/antlr/src/main/groovy/org/gradle/api/plugins/antlr/internal/GrammarFileMetadata.java
+++ b/subprojects/antlr/src/main/groovy/org/gradle/api/plugins/antlr/internal/GrammarFileMetadata.java
@@ -15,15 +15,13 @@
  */
 package org.gradle.api.plugins.antlr.internal;
 
-import java.util.List;
-import java.util.ArrayList;
 import java.io.File;
+import java.util.ArrayList;
+import java.util.List;
 
 /**
  * Models information about an Antlr grammar file, including the inidividual {@link #getGrammars grammars} (lexers,
  * parsers, etc) contained within it.
- *
- * @author Steve Ebersole
  */
 public class GrammarFileMetadata {
     private final File filePath;
diff --git a/subprojects/antlr/src/main/groovy/org/gradle/api/plugins/antlr/internal/GrammarMetadata.java b/subprojects/antlr/src/main/groovy/org/gradle/api/plugins/antlr/internal/GrammarMetadata.java
index 796370f..371715e 100644
--- a/subprojects/antlr/src/main/groovy/org/gradle/api/plugins/antlr/internal/GrammarMetadata.java
+++ b/subprojects/antlr/src/main/groovy/org/gradle/api/plugins/antlr/internal/GrammarMetadata.java
@@ -15,15 +15,13 @@
  */
 package org.gradle.api.plugins.antlr.internal;
 
-import java.io.File;
-
-import antlr.TreeParser;
 import antlr.Parser;
+import antlr.TreeParser;
+
+import java.io.File;
 
 /**
  * Models a grammar defined within an Antlr grammar file.
- *
- * @author Steve Ebersole
  */
 public class GrammarMetadata {
     private final GrammarFileMetadata grammarFileMetadata;
diff --git a/subprojects/antlr/src/main/groovy/org/gradle/api/plugins/antlr/internal/MetadataExtracter.java b/subprojects/antlr/src/main/groovy/org/gradle/api/plugins/antlr/internal/MetadataExtracter.java
index 35e480f..fee4fd8 100644
--- a/subprojects/antlr/src/main/groovy/org/gradle/api/plugins/antlr/internal/MetadataExtracter.java
+++ b/subprojects/antlr/src/main/groovy/org/gradle/api/plugins/antlr/internal/MetadataExtracter.java
@@ -15,20 +15,14 @@
  */
 package org.gradle.api.plugins.antlr.internal;
 
-import java.io.File;
-import java.io.BufferedReader;
-import java.io.FileReader;
-import java.io.IOException;
-import java.io.FileNotFoundException;
-
 import org.gradle.api.UncheckedIOException;
 import org.gradle.api.file.FileTree;
 
+import java.io.*;
+
 /**
  * Preprocess an Antlr grammar file so that dependencies between grammars can be properly determined such that they can
  * be processed for generation in proper order later.
- *
- * @author Steve Ebersole
  */
 public class MetadataExtracter {
 
diff --git a/subprojects/antlr/src/main/groovy/org/gradle/api/plugins/antlr/internal/XRef.java b/subprojects/antlr/src/main/groovy/org/gradle/api/plugins/antlr/internal/XRef.java
index 2b73ded..976fee9 100644
--- a/subprojects/antlr/src/main/groovy/org/gradle/api/plugins/antlr/internal/XRef.java
+++ b/subprojects/antlr/src/main/groovy/org/gradle/api/plugins/antlr/internal/XRef.java
@@ -15,17 +15,15 @@
  */
 package org.gradle.api.plugins.antlr.internal;
 
-import java.util.LinkedHashMap;
+import antlr.preprocessor.Hierarchy;
+
 import java.util.HashMap;
 import java.util.Iterator;
-
-import antlr.preprocessor.Hierarchy;
+import java.util.LinkedHashMap;
 
 /**
  * Models cross-reference (x-ref) info about {@link GrammarFileMetadata grammar files} such as {@link #filesByPath},
  * {@link #filesByExportVocab} and {@link #filesByClassName}.
- *
- * @author Steve Ebersole
  */
 public class XRef {
     private final Hierarchy antlrHierarchy;
diff --git a/subprojects/antlr/src/test/groovy/org/gradle/api/plugins/antlr/AntlrPluginTest.groovy b/subprojects/antlr/src/test/groovy/org/gradle/api/plugins/antlr/AntlrPluginTest.groovy
index b94619d..42fccb2 100644
--- a/subprojects/antlr/src/test/groovy/org/gradle/api/plugins/antlr/AntlrPluginTest.groovy
+++ b/subprojects/antlr/src/test/groovy/org/gradle/api/plugins/antlr/AntlrPluginTest.groovy
@@ -18,10 +18,10 @@ package org.gradle.api.plugins.antlr
 
 import spock.lang.Specification
 import org.gradle.api.Project
-import org.gradle.util.HelperUtil
+import org.gradle.util.TestUtil
 
 class AntlrPluginTest extends Specification {
-    private final Project project = HelperUtil.createRootProject()
+    private final Project project = TestUtil.createRootProject()
 
     def addsAntlrPropertiesToEachSourceSet() {
         when:
@@ -35,7 +35,7 @@ class AntlrPluginTest extends Specification {
         test.antlr.srcDirs == [project.file('src/test/antlr')] as Set
 
         when:
-        project.sourceSets.add('custom')
+        project.sourceSets.create('custom')
 
         then:
         def custom = project.sourceSets.custom
@@ -56,7 +56,7 @@ class AntlrPluginTest extends Specification {
         project.tasks.compileTestJava.taskDependencies.getDependencies(null).contains(test)
 
         when:
-        project.sourceSets.add('custom')
+        project.sourceSets.create('custom')
 
         then:
         def custom = project.tasks.generateCustomGrammarSource
diff --git a/subprojects/base-services-groovy/base-services-groovy.gradle b/subprojects/base-services-groovy/base-services-groovy.gradle
index 90d6a16..c50fe5d 100644
--- a/subprojects/base-services-groovy/base-services-groovy.gradle
+++ b/subprojects/base-services-groovy/base-services-groovy.gradle
@@ -15,7 +15,7 @@
  */
 
 dependencies {
-    groovy libraries.groovy
+    compile libraries.groovy
     compile project(":baseServices")
 }
 
diff --git a/subprojects/base-services-groovy/src/main/groovy/org/gradle/api/specs/AndSpec.java b/subprojects/base-services-groovy/src/main/groovy/org/gradle/api/specs/AndSpec.java
index 7da1f73..90cce09 100644
--- a/subprojects/base-services-groovy/src/main/groovy/org/gradle/api/specs/AndSpec.java
+++ b/subprojects/base-services-groovy/src/main/groovy/org/gradle/api/specs/AndSpec.java
@@ -26,7 +26,6 @@ import java.util.List;
  * A {@link org.gradle.api.specs.CompositeSpec} which requires all its specs to be true in order to evaluate to true.
  * Uses lazy evaluation.
  *
- * @author Hans Dockter
  * @param <T> The target type for this Spec
  */
 public class AndSpec<T> extends CompositeSpec<T> {
diff --git a/subprojects/base-services-groovy/src/test/groovy/org/gradle/api/specs/AndSpecTest.java b/subprojects/base-services-groovy/src/test/groovy/org/gradle/api/specs/AndSpecTest.java
index a26dda9..37f447b 100644
--- a/subprojects/base-services-groovy/src/test/groovy/org/gradle/api/specs/AndSpecTest.java
+++ b/subprojects/base-services-groovy/src/test/groovy/org/gradle/api/specs/AndSpecTest.java
@@ -15,7 +15,7 @@
  */
 package org.gradle.api.specs;
 
-import org.gradle.util.HelperUtil;
+import org.gradle.util.TestUtil;
 import org.junit.Test;
 
 import static org.junit.Assert.assertFalse;
@@ -51,7 +51,7 @@ public class AndSpecTest extends AbstractCompositeSpecTest {
     @Test
     public void canAddClosureAsASpec() {
         AndSpec<Object> spec = new AndSpec<Object>(createAtomicElements(true));
-        spec = spec.and(HelperUtil.toClosure("{ false }"));
+        spec = spec.and(TestUtil.toClosure("{ false }"));
         assertFalse(spec.isSatisfiedBy(new Object()));
     }
 }
diff --git a/subprojects/base-services/base-services.gradle b/subprojects/base-services/base-services.gradle
index f825ab8..2a2dcff 100644
--- a/subprojects/base-services/base-services.gradle
+++ b/subprojects/base-services/base-services.gradle
@@ -5,9 +5,9 @@
  * application (eg as part of the tooling API).
  */
 dependencies {
-    groovy libraries.groovy
-
     publishCompile libraries.slf4j_api
+    compile libraries.guava
+    testCompile libraries.groovy
 }
 
 useTestFixtures()
diff --git a/subprojects/base-services/src/main/java/org/gradle/api/GradleException.java b/subprojects/base-services/src/main/java/org/gradle/api/GradleException.java
index 788305f..430a702 100644
--- a/subprojects/base-services/src/main/java/org/gradle/api/GradleException.java
+++ b/subprojects/base-services/src/main/java/org/gradle/api/GradleException.java
@@ -18,8 +18,6 @@ package org.gradle.api;
 
 /**
  * <p><code>GradleException</code> is the base class of all exceptions thrown by Gradle.</p>
- *
- * @author Hans Dockter
  */
 public class GradleException extends RuntimeException {
     public GradleException() {
diff --git a/subprojects/base-services/src/main/java/org/gradle/api/JavaVersion.java b/subprojects/base-services/src/main/java/org/gradle/api/JavaVersion.java
index b1b430f..9804ed5 100644
--- a/subprojects/base-services/src/main/java/org/gradle/api/JavaVersion.java
+++ b/subprojects/base-services/src/main/java/org/gradle/api/JavaVersion.java
@@ -114,4 +114,8 @@ public enum JavaVersion {
     private String getName() {
         return name().substring("VERSION_".length()).replace('_', '.');
     }
+
+    public String getMajorVersion() {
+        return name().substring(10);
+    }
 }
diff --git a/subprojects/base-services/src/main/java/org/gradle/api/internal/Factory.java b/subprojects/base-services/src/main/java/org/gradle/api/internal/Factory.java
index 0e4242e..7464292 100644
--- a/subprojects/base-services/src/main/java/org/gradle/api/internal/Factory.java
+++ b/subprojects/base-services/src/main/java/org/gradle/api/internal/Factory.java
@@ -16,8 +16,10 @@
 package org.gradle.api.internal;
 
 /**
+ * DO NOT REMOVE.
+ *
  * @deprecated This is here because tasks implemented in Groovy that are compiled against older versions of Gradle have this type baked into their byte-code, and cannot be loaded if it's not found.
  */
 @Deprecated
-public interface Factory<T> extends org.gradle.internal.Factory<T> {
+public interface Factory<T> {
 }
diff --git a/subprojects/base-services/src/main/java/org/gradle/api/internal/Transformers.java b/subprojects/base-services/src/main/java/org/gradle/api/internal/Transformers.java
index c066c8e..cc94eae 100644
--- a/subprojects/base-services/src/main/java/org/gradle/api/internal/Transformers.java
+++ b/subprojects/base-services/src/main/java/org/gradle/api/internal/Transformers.java
@@ -16,9 +16,15 @@
 
 package org.gradle.api.internal;
 
+import org.gradle.api.Action;
 import org.gradle.api.Named;
 import org.gradle.api.Namer;
 import org.gradle.api.Transformer;
+import org.gradle.internal.UncheckedException;
+
+import java.net.MalformedURLException;
+import java.net.URI;
+import java.net.URL;
 
 /**
  * Utility transformers.
@@ -92,4 +98,46 @@ public abstract class Transformers {
         }
     }
 
+    /**
+     * A getClass() transformer.
+     *
+     * @param <T> The type of the object
+     * @return A getClass() transformer.
+     */
+    public static <T> Transformer<Class<T>, T> type() {
+        return new Transformer<Class<T>, T>() {
+            public Class<T> transform(T original) {
+                @SuppressWarnings("unchecked")
+                Class<T> aClass = (Class<T>) original.getClass();
+                return aClass;
+            }
+        };
+    }
+
+    /**
+     * Converts an {@link Action} to a {@link Transformer} that runs the action against the input value and returns {@code null}.
+     */
+    public static <R, I> Transformer<R, I> toTransformer(final Action<? super I> action) {
+        return new Transformer<R, I>() {
+            public R transform(I original) {
+                action.execute(original);
+                return null;
+            }
+        };
+    }
+
+    /**
+     * Converts a URL to a URI
+     */
+    public static Transformer<URL, URI> toURL() {
+        return new Transformer<URL, URI>() {
+            public URL transform(URI original) {
+                try {
+                    return original.toURL();
+                } catch (MalformedURLException e) {
+                    throw UncheckedException.throwAsUncheckedException(e);
+                }
+            }
+        };
+    }
 }
diff --git a/subprojects/base-services/src/main/java/org/gradle/api/internal/project/ServiceRegistry.java b/subprojects/base-services/src/main/java/org/gradle/api/internal/project/ServiceRegistry.java
index 92825aa..2f45094 100644
--- a/subprojects/base-services/src/main/java/org/gradle/api/internal/project/ServiceRegistry.java
+++ b/subprojects/base-services/src/main/java/org/gradle/api/internal/project/ServiceRegistry.java
@@ -16,8 +16,10 @@
 package org.gradle.api.internal.project;
 
 /**
+ * DO NOT REMOVE.
+ *
  * @deprecated This is here because tasks implemented in Groovy that are compiled against older versions of Gradle have this type baked into their byte-code, and cannot be loaded if it's not found.
  */
 @Deprecated
-public interface ServiceRegistry extends org.gradle.internal.service.ServiceRegistry {
+public interface ServiceRegistry {
 }
diff --git a/subprojects/base-services/src/main/java/org/gradle/api/specs/CompositeSpec.java b/subprojects/base-services/src/main/java/org/gradle/api/specs/CompositeSpec.java
index f3ce645..b605164 100644
--- a/subprojects/base-services/src/main/java/org/gradle/api/specs/CompositeSpec.java
+++ b/subprojects/base-services/src/main/java/org/gradle/api/specs/CompositeSpec.java
@@ -23,7 +23,6 @@ import java.util.List;
 /**
  * A {@link org.gradle.api.specs.Spec} which aggregates a sequence of other {@code Spec} instances.
  *
- * @author Hans Dockter
  * @param <T> The target type for this Spec
  */
 abstract public class CompositeSpec<T> implements Spec<T> {
diff --git a/subprojects/base-services/src/main/java/org/gradle/api/specs/NotSpec.java b/subprojects/base-services/src/main/java/org/gradle/api/specs/NotSpec.java
index 6938d8b..be6db06 100644
--- a/subprojects/base-services/src/main/java/org/gradle/api/specs/NotSpec.java
+++ b/subprojects/base-services/src/main/java/org/gradle/api/specs/NotSpec.java
@@ -18,7 +18,6 @@ package org.gradle.api.specs;
 /**
  * A {@link org.gradle.api.specs.Spec} implementation which negates another {@code Spec}.
  * 
- * @author Hans Dockter
  * @param <T> The target type for this Spec
  */
 public class NotSpec<T> implements Spec<T> {
diff --git a/subprojects/base-services/src/main/java/org/gradle/api/specs/OrSpec.java b/subprojects/base-services/src/main/java/org/gradle/api/specs/OrSpec.java
index 9f63a61..3c84c8c 100644
--- a/subprojects/base-services/src/main/java/org/gradle/api/specs/OrSpec.java
+++ b/subprojects/base-services/src/main/java/org/gradle/api/specs/OrSpec.java
@@ -21,7 +21,6 @@ import java.util.List;
  * A {@link CompositeSpec} which requires any one of its specs to be true in order to evaluate to
  * true. Uses lazy evaluation.
  *
- * @author Hans Dockter
  * @param <T> The target type for this Spec
  */
 public class OrSpec<T> extends CompositeSpec<T> {
diff --git a/subprojects/base-services/src/main/java/org/gradle/api/specs/Spec.java b/subprojects/base-services/src/main/java/org/gradle/api/specs/Spec.java
index a6522b4..ebb6ae7 100644
--- a/subprojects/base-services/src/main/java/org/gradle/api/specs/Spec.java
+++ b/subprojects/base-services/src/main/java/org/gradle/api/specs/Spec.java
@@ -18,7 +18,6 @@ package org.gradle.api.specs;
 /**
  * Represents some predicate against objects of type T.
  *
- * @author Hans Dockter
  * @param <T> The target type for this Spec
  */
 public interface Spec<T> {
diff --git a/subprojects/base-services/src/main/java/org/gradle/internal/LazyIterable.java b/subprojects/base-services/src/main/java/org/gradle/internal/LazyIterable.java
deleted file mode 100644
index 74ea26d..0000000
--- a/subprojects/base-services/src/main/java/org/gradle/internal/LazyIterable.java
+++ /dev/null
@@ -1,33 +0,0 @@
-/*
- * Copyright 2012 the original author or authors.
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- *      http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package org.gradle.internal;
-
-import java.util.Iterator;
-
-public class LazyIterable<T> implements Iterable<T> {
-
-    private final Factory<Iterable<T>> factory;
-
-    public LazyIterable(Factory<Iterable<T>> factory) {
-        this.factory = factory;
-    }
-
-    public Iterator<T> iterator() {
-        return factory.create().iterator();
-    }
-
-}
diff --git a/subprojects/base-services/src/main/java/org/gradle/internal/classloader/CachingClassLoader.java b/subprojects/base-services/src/main/java/org/gradle/internal/classloader/CachingClassLoader.java
new file mode 100644
index 0000000..7bad876
--- /dev/null
+++ b/subprojects/base-services/src/main/java/org/gradle/internal/classloader/CachingClassLoader.java
@@ -0,0 +1,66 @@
+/*
+ * Copyright 2013 the original author or authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.gradle.internal.classloader;
+
+import com.google.common.collect.MapMaker;
+
+import java.util.concurrent.ConcurrentMap;
+
+public class CachingClassLoader extends ClassLoader implements ClassLoaderHierarchy {
+    private static final Object MISSING_CLASS = new Object();
+    private final ConcurrentMap<String, Object> loadedClasses = new MapMaker().weakValues().makeMap();
+
+    public CachingClassLoader(ClassLoader parent) {
+        super(parent);
+    }
+
+    @Override
+    protected Class<?> loadClass(String name, boolean resolve) throws ClassNotFoundException {
+        Object cachedValue = loadedClasses.get(name);
+        if (cachedValue instanceof Class) {
+            return (Class<?>) cachedValue;
+        } else if (cachedValue == MISSING_CLASS) {
+            throw new ClassNotFoundException(name);
+        }
+        Class<?> result;
+        try {
+            result = super.loadClass(name, resolve);
+        } catch (ClassNotFoundException e) {
+            loadedClasses.putIfAbsent(name, MISSING_CLASS);
+            throw e;
+        }
+        loadedClasses.putIfAbsent(name, result);
+        return result;
+    }
+
+    public void visit(ClassLoaderVisitor visitor) {
+        visitor.visitSpec(new Spec());
+        visitor.visitParent(getParent());
+    }
+
+    public static class Spec extends ClassLoaderSpec {
+        @Override
+        public boolean equals(Object obj) {
+            return obj != null && obj.getClass().equals(Spec.class);
+        }
+
+        @Override
+        public int hashCode() {
+            return getClass().getName().hashCode();
+        }
+    }
+}
diff --git a/subprojects/base-services/src/main/java/org/gradle/internal/classloader/ClassLoaderFactory.java b/subprojects/base-services/src/main/java/org/gradle/internal/classloader/ClassLoaderFactory.java
new file mode 100644
index 0000000..90d37a8
--- /dev/null
+++ b/subprojects/base-services/src/main/java/org/gradle/internal/classloader/ClassLoaderFactory.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.internal.classloader;
+
+import org.gradle.internal.classpath.ClassPath;
+
+import java.net.URI;
+import java.util.List;
+
+public interface ClassLoaderFactory {
+    /**
+     * Creates a ClassLoader implementation which has only the classes from the specified URIs and the Java API visible.
+     */
+    ClassLoader createIsolatedClassLoader(ClassPath classPath);
+
+    /**
+     * Creates a ClassLoader implementation which has only the classes from the specified URIs and the Java API visible.
+     */
+    ClassLoader createIsolatedClassLoader(Iterable<URI> uris);
+
+    /**
+     * Creates a ClassLoader implementation which has, by default, only the classes from the Java API visible, but which can allow access
+     * to selected classes from the given parent ClassLoader.
+     *
+     * @param parent the parent ClassLoader
+     * @return The ClassLoader
+     */
+    FilteringClassLoader createFilteringClassLoader(ClassLoader parent);
+
+    /**
+     * Creates a ClassLoader from its spec.
+     */
+    ClassLoader createClassLoader(ClassLoaderSpec spec, List<? extends ClassLoader> parents);
+}
diff --git a/subprojects/base-services/src/main/java/org/gradle/internal/classloader/ClassLoaderHierarchy.java b/subprojects/base-services/src/main/java/org/gradle/internal/classloader/ClassLoaderHierarchy.java
new file mode 100644
index 0000000..241c0ac
--- /dev/null
+++ b/subprojects/base-services/src/main/java/org/gradle/internal/classloader/ClassLoaderHierarchy.java
@@ -0,0 +1,21 @@
+/*
+ * Copyright 2013 the original author or authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.gradle.internal.classloader;
+
+public interface ClassLoaderHierarchy {
+    void visit(ClassLoaderVisitor visitor);
+}
diff --git a/subprojects/base-services/src/main/java/org/gradle/internal/classloader/ClassLoaderSpec.java b/subprojects/base-services/src/main/java/org/gradle/internal/classloader/ClassLoaderSpec.java
new file mode 100644
index 0000000..8b96204
--- /dev/null
+++ b/subprojects/base-services/src/main/java/org/gradle/internal/classloader/ClassLoaderSpec.java
@@ -0,0 +1,45 @@
+/*
+ * Copyright 2013 the original author or authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.gradle.internal.classloader;
+
+import java.io.Serializable;
+
+/**
+ * An immutable description of a ClassLoader hierarchy that can be used to recreate the hierarchy in a different process.
+ *
+ * Subclasses should implement equals() and hashCode(), so that the spec can be used as a hashmap key.
+ */
+public abstract class ClassLoaderSpec implements Serializable {
+    public static final ClassLoaderSpec SYSTEM_CLASS_LOADER = new SystemClassLoaderSpec();
+
+    private static class SystemClassLoaderSpec extends ClassLoaderSpec {
+        @Override
+        public boolean equals(Object obj) {
+            return obj != null && obj.getClass().equals(getClass());
+        }
+
+        @Override
+        public String toString() {
+            return "system";
+        }
+
+        @Override
+        public int hashCode() {
+            return 121;
+        }
+    }
+}
diff --git a/subprojects/base-services/src/main/java/org/gradle/internal/classloader/ClassLoaderVisitor.java b/subprojects/base-services/src/main/java/org/gradle/internal/classloader/ClassLoaderVisitor.java
new file mode 100644
index 0000000..92cc137
--- /dev/null
+++ b/subprojects/base-services/src/main/java/org/gradle/internal/classloader/ClassLoaderVisitor.java
@@ -0,0 +1,52 @@
+/*
+ * Copyright 2013 the original author or authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.gradle.internal.classloader;
+
+import java.net.URL;
+import java.net.URLClassLoader;
+
+public class ClassLoaderVisitor {
+    private final ClassLoader stopAt = ClassLoader.getSystemClassLoader() == null ? null : ClassLoader.getSystemClassLoader().getParent();
+
+    public void visit(ClassLoader classLoader) {
+        if (classLoader == stopAt) {
+            visitSpec(ClassLoaderSpec.SYSTEM_CLASS_LOADER);
+            return;
+        }
+
+        if (classLoader instanceof ClassLoaderHierarchy) {
+            ((ClassLoaderHierarchy) classLoader).visit(this);
+        } else {
+            if (classLoader instanceof URLClassLoader) {
+                visitClassPath(((URLClassLoader) classLoader).getURLs());
+            }
+            if (classLoader.getParent() != null) {
+                visitParent(classLoader.getParent());
+            }
+        }
+    }
+
+    public void visitSpec(ClassLoaderSpec spec) {
+    }
+
+    public void visitClassPath(URL[] classPath) {
+    }
+
+    public void visitParent(ClassLoader classLoader) {
+        visit(classLoader);
+    }
+}
diff --git a/subprojects/base-services/src/main/java/org/gradle/internal/classloader/ClasspathUtil.java b/subprojects/base-services/src/main/java/org/gradle/internal/classloader/ClasspathUtil.java
new file mode 100644
index 0000000..f1e2ac6
--- /dev/null
+++ b/subprojects/base-services/src/main/java/org/gradle/internal/classloader/ClasspathUtil.java
@@ -0,0 +1,101 @@
+/*
+ * Copyright 2010 the original author or authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.gradle.internal.classloader;
+
+import org.gradle.api.GradleException;
+import org.gradle.internal.UncheckedException;
+
+import java.io.File;
+import java.lang.reflect.Method;
+import java.net.URI;
+import java.net.URISyntaxException;
+import java.net.URL;
+import java.net.URLClassLoader;
+import java.util.ArrayList;
+import java.util.Arrays;
+import java.util.List;
+
+public class ClasspathUtil {
+    public static void addUrl(URLClassLoader classLoader, Iterable<URL> classpathElements) {
+        try {
+            Method method = URLClassLoader.class.getDeclaredMethod("addURL", URL.class);
+            method.setAccessible(true);
+            for (URL classpathElement : classpathElements) {
+                method.invoke(classLoader, classpathElement);
+            }
+        } catch (Throwable t) {
+            throw new RuntimeException("Error, could not add URL to classloader", t);
+        }
+    }
+
+    public static List<URL> getClasspath(ClassLoader classLoader) {
+        final List<URL> implementationClassPath = new ArrayList<URL>();
+        new ClassLoaderVisitor() {
+            @Override
+            public void visitClassPath(URL[] classPath) {
+                implementationClassPath.addAll(Arrays.asList(classPath));
+            }
+        }.visit(classLoader);
+        return implementationClassPath;
+    }
+
+    public static File getClasspathForClass(Class<?> targetClass) {
+        URI location;
+        try {
+            location = targetClass.getProtectionDomain().getCodeSource().getLocation().toURI();
+        } catch (URISyntaxException e) {
+            throw UncheckedException.throwAsUncheckedException(e);
+        }
+        if (!location.getScheme().equals("file")) {
+            throw new GradleException(String.format("Cannot determine classpath for %s from codebase '%s'.", targetClass.getName(), location));
+        }
+        return new File(location.getPath());
+    }
+
+    public static File getClasspathForResource(ClassLoader classLoader, String name) {
+        if (classLoader == null) {
+            return getClasspathForResource(ClassLoader.getSystemResource(name), name);
+        } else {
+            return getClasspathForResource(classLoader.getResource(name), name);
+        }
+    }
+
+    public static File getClasspathForResource(URL resource, String name) {
+        URI location;
+        try {
+            location = resource.toURI();
+            String path = location.getPath();
+            if (location.getScheme().equals("file")) {
+                assert path.endsWith("/" + name);
+                return new File(path.substring(0, path.length() - (name.length() + 1)));
+            } else if (location.getScheme().equals("jar")) {
+                String schemeSpecificPart = location.getRawSchemeSpecificPart();
+                int pos = schemeSpecificPart.indexOf("!");
+                if (pos > 0) {
+                    assert schemeSpecificPart.substring(pos + 1).equals("/" + name);
+                    URI jarFile = new URI(schemeSpecificPart.substring(0, pos));
+                    if (jarFile.getScheme().equals("file")) {
+                        return new File(jarFile.getPath());
+                    }
+                }
+            }
+        } catch (URISyntaxException e) {
+            throw UncheckedException.throwAsUncheckedException(e);
+        }
+        throw new GradleException(String.format("Cannot determine classpath for resource '%s' from location '%s'.", name, location));
+    }
+}
diff --git a/subprojects/base-services/src/main/java/org/gradle/internal/classloader/DefaultClassLoaderFactory.java b/subprojects/base-services/src/main/java/org/gradle/internal/classloader/DefaultClassLoaderFactory.java
new file mode 100644
index 0000000..68dc2d5
--- /dev/null
+++ b/subprojects/base-services/src/main/java/org/gradle/internal/classloader/DefaultClassLoaderFactory.java
@@ -0,0 +1,111 @@
+/*
+ * Copyright 2011 the original author or authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.gradle.internal.classloader;
+
+import org.gradle.api.internal.Transformers;
+import org.gradle.internal.UncheckedException;
+import org.gradle.internal.classpath.ClassPath;
+import org.gradle.internal.service.ServiceLocator;
+import org.gradle.util.CollectionUtils;
+
+import javax.xml.datatype.DatatypeFactory;
+import javax.xml.parsers.DocumentBuilderFactory;
+import javax.xml.parsers.SAXParserFactory;
+import java.net.MalformedURLException;
+import java.net.URI;
+import java.net.URL;
+import java.net.URLClassLoader;
+import java.util.Collection;
+import java.util.List;
+
+public class DefaultClassLoaderFactory implements ClassLoaderFactory {
+    public ClassLoader createIsolatedClassLoader(Iterable<URI> uris) {
+        return doCreateIsolatedClassLoader(CollectionUtils.collect(uris, Transformers.toURL()));
+    }
+
+    public ClassLoader createIsolatedClassLoader(ClassPath classPath) {
+        return doCreateIsolatedClassLoader(classPath.getAsURLs());
+    }
+
+    private ClassLoader doCreateIsolatedClassLoader(Collection<URL> classpath) {
+        // This piece of ugliness copies the JAXP (ie XML API) provider, if any, from the system ClassLoader. Here's why:
+        //
+        // 1. When looking for a provider, JAXP looks for a service resource in the context ClassLoader, which is our isolated ClassLoader. If our classpath above does not contain a
+        //    provider, this returns null. If it does contain a provider, JAXP extracts the classname from the service resource.
+        // 2. If not found, JAXP looks for a service resource in the system ClassLoader. This happens to include all the application classes specified on the classpath. If the application
+        //    classpath does not contain a provider, this returns null. If it does contain a provider, JAXP extracts the implementation classname from the service resource.
+        // 3. If not found, JAXP uses a default classname
+        // 4. JAXP attempts to load the provider using the context ClassLoader. which is our isolated ClassLoader. This is fine if the classname came from step 1 or 3. It blows up if the
+        //    classname came from step 2.
+        //
+        // So, as a workaround, locate and include the JAXP provider jar in the classpath for our isolated ClassLoader.
+        //
+        // Note that in practise, this is only triggered when running in our tests
+
+        if (needJaxpImpl()) {
+            try {
+                classpath.add(ClasspathUtil.getClasspathForResource(ClassLoader.getSystemClassLoader(), "META-INF/services/javax.xml.parsers.SAXParserFactory").toURI().toURL());
+            } catch (MalformedURLException e) {
+                throw UncheckedException.throwAsUncheckedException(e);
+            }
+        }
+
+        return new URLClassLoader(classpath.toArray(new URL[classpath.size()]), ClassLoader.getSystemClassLoader().getParent());
+    }
+
+    public FilteringClassLoader createFilteringClassLoader(ClassLoader parent) {
+        // See the comment for {@link #createIsolatedClassLoader} above
+        FilteringClassLoader classLoader = new FilteringClassLoader(parent);
+        if (needJaxpImpl()) {
+            ServiceLocator locator = new ServiceLocator(ClassLoader.getSystemClassLoader());
+            makeServiceVisible(locator, classLoader, SAXParserFactory.class);
+            makeServiceVisible(locator, classLoader, DocumentBuilderFactory.class);
+            makeServiceVisible(locator, classLoader, DatatypeFactory.class);
+        }
+        return classLoader;
+    }
+
+    public ClassLoader createClassLoader(ClassLoaderSpec spec, List<? extends ClassLoader> parents) {
+        if (spec instanceof MultiParentClassLoader.Spec) {
+            return new MultiParentClassLoader(parents);
+        }
+        if (parents.size() != 1) {
+            throw new IllegalArgumentException("Expected a single parent.");
+        }
+        ClassLoader parent = parents.get(0);
+        if (spec instanceof MutableURLClassLoader.Spec) {
+            MutableURLClassLoader.Spec clSpec = (MutableURLClassLoader.Spec) spec;
+            return new MutableURLClassLoader(parent, clSpec);
+        }
+        if (spec instanceof CachingClassLoader.Spec) {
+            return new CachingClassLoader(parent);
+        }
+        if (spec instanceof FilteringClassLoader.Spec) {
+            FilteringClassLoader.Spec clSpec = (FilteringClassLoader.Spec) spec;
+            return new FilteringClassLoader(parent, clSpec);
+        }
+        throw new IllegalArgumentException(String.format("Don't know how to create a ClassLoader from spec %s", spec));
+    }
+
+    private void makeServiceVisible(ServiceLocator locator, FilteringClassLoader classLoader, Class<?> serviceType) {
+        classLoader.allowClass(locator.getFactory(serviceType).getImplementationClass());
+        classLoader.allowResource("META-INF/services/" + serviceType.getName());
+    }
+
+    private boolean needJaxpImpl() {
+        return ClassLoader.getSystemResource("META-INF/services/javax.xml.parsers.SAXParserFactory") != null;
+    }
+}
diff --git a/subprojects/base-services/src/main/java/org/gradle/internal/classloader/FilteringClassLoader.java b/subprojects/base-services/src/main/java/org/gradle/internal/classloader/FilteringClassLoader.java
new file mode 100644
index 0000000..6ffa40a
--- /dev/null
+++ b/subprojects/base-services/src/main/java/org/gradle/internal/classloader/FilteringClassLoader.java
@@ -0,0 +1,275 @@
+/*
+ * Copyright 2012 the original author or authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.gradle.internal.classloader;
+
+import org.gradle.internal.reflect.JavaReflectionUtil;
+import org.gradle.util.JavaMethod;
+
+import java.io.IOException;
+import java.net.URL;
+import java.security.AccessController;
+import java.security.PrivilegedAction;
+import java.util.*;
+
+/**
+ * A ClassLoader which hides all non-system classes, packages and resources. Allows certain non-system packages and classes to be declared as visible. By default, only the Java system classes,
+ * packages and resources are visible.
+ */
+public class FilteringClassLoader extends ClassLoader implements ClassLoaderHierarchy {
+    private static final Set<ClassLoader> SYSTEM_CLASS_LOADERS = new HashSet<ClassLoader>();
+    private static final ClassLoader EXT_CLASS_LOADER;
+    private static final Set<String> SYSTEM_PACKAGES = new HashSet<String>();
+    private final Set<String> packageNames = new HashSet<String>();
+    private final Set<String> packagePrefixes = new HashSet<String>();
+    private final Set<String> resourcePrefixes = new HashSet<String>();
+    private final Set<String> resourceNames = new HashSet<String>();
+    private final Set<String> classNames = new HashSet<String>();
+    private final Set<String> disallowedClassNames = new HashSet<String>();
+
+    static {
+        EXT_CLASS_LOADER = ClassLoader.getSystemClassLoader().getParent();
+        for (ClassLoader cl = EXT_CLASS_LOADER; cl != null; cl = cl.getParent()) {
+            SYSTEM_CLASS_LOADERS.add(cl);
+        }
+        JavaMethod<ClassLoader, Package[]> method = JavaReflectionUtil.method(ClassLoader.class, Package[].class, "getPackages");
+        Package[] systemPackages = method.invoke(EXT_CLASS_LOADER);
+        for (Package p : systemPackages) {
+            SYSTEM_PACKAGES.add(p.getName());
+        }
+    }
+
+    public FilteringClassLoader(ClassLoader parent) {
+        super(parent);
+    }
+
+    public FilteringClassLoader(ClassLoader parent, Spec spec) {
+        super(parent);
+        packageNames.addAll(spec.packageNames);
+        packagePrefixes.addAll(spec.packagePrefixes);
+        resourceNames.addAll(spec.resourceNames);
+        resourcePrefixes.addAll(spec.resourcePrefixes);
+        classNames.addAll(spec.classNames);
+        disallowedClassNames.addAll(spec.classNames);
+    }
+
+    public void visit(ClassLoaderVisitor visitor) {
+        visitor.visitSpec(new Spec(classNames, packageNames, packagePrefixes, resourcePrefixes, resourceNames, disallowedClassNames));
+        visitor.visitParent(getParent());
+    }
+
+    @Override
+    protected Class<?> loadClass(String name, boolean resolve) throws ClassNotFoundException {
+        Class<?> cl;
+        try {
+            cl = super.loadClass(name, false);
+        } catch (NoClassDefFoundError e) {
+            if (classAllowed(name)) {
+                throw e;
+            }
+            // The class isn't visible
+            throw new ClassNotFoundException(String.format("%s not found.", name));
+        }
+
+        if (!allowed(cl)) {
+            throw new ClassNotFoundException(String.format("%s not found.", cl.getName()));
+        }
+        if (resolve) {
+            resolveClass(cl);
+        }
+
+        return cl;
+    }
+
+    @Override
+    protected Package getPackage(String name) {
+        Package p = super.getPackage(name);
+        if (p == null || !allowed(p)) {
+            return null;
+        }
+        return p;
+    }
+
+    @Override
+    protected Package[] getPackages() {
+        List<Package> packages = new ArrayList<Package>();
+        for (Package p : super.getPackages()) {
+            if (allowed(p)) {
+                packages.add(p);
+            }
+        }
+        return packages.toArray(new Package[packages.size()]);
+    }
+
+    @Override
+    public URL getResource(String name) {
+        if (allowed(name)) {
+            return super.getResource(name);
+        }
+        return EXT_CLASS_LOADER.getResource(name);
+    }
+
+    @Override
+    public Enumeration<URL> getResources(String name) throws IOException {
+        if (allowed(name)) {
+            return super.getResources(name);
+        }
+        return EXT_CLASS_LOADER.getResources(name);
+    }
+
+    private boolean allowed(String resourceName) {
+        if (resourceNames.contains(resourceName)) {
+            return true;
+        }
+        for (String resourcePrefix : resourcePrefixes) {
+            if (resourceName.startsWith(resourcePrefix)) {
+                return true;
+            }
+        }
+        return false;
+    }
+
+    private boolean allowed(Package pkg) {
+        if (SYSTEM_PACKAGES.contains(pkg.getName())) {
+            return true;
+        }
+        if (packageNames.contains(pkg.getName())) {
+            return true;
+        }
+        for (String packagePrefix : packagePrefixes) {
+            if (pkg.getName().startsWith(packagePrefix)) {
+                return true;
+            }
+        }
+        return false;
+    }
+
+    private boolean allowed(final Class<?> clazz) {
+        boolean systemClass = AccessController.doPrivileged(new PrivilegedAction<Boolean>() {
+            public Boolean run() {
+                return clazz.getClassLoader() == null || SYSTEM_CLASS_LOADERS.contains(clazz.getClassLoader());
+            }
+        });
+        return systemClass || classAllowed(clazz.getName());
+    }
+
+    private boolean classAllowed(String className) {
+        if (disallowedClassNames.contains(className)) {
+            return false;
+        }
+        if (classNames.contains(className)) {
+            return true;
+        }
+        for (String packagePrefix : packagePrefixes) {
+            if (className.startsWith(packagePrefix)) {
+                return true;
+            }
+        }
+        return false;
+    }
+
+    /**
+     * Marks a package and all its sub-packages as visible. Also makes resources in those packages visible.
+     *
+     * @param packageName the package name
+     */
+    public void allowPackage(String packageName) {
+        packageNames.add(packageName);
+        packagePrefixes.add(packageName + ".");
+        resourcePrefixes.add(packageName.replace('.', '/') + '/');
+    }
+
+    /**
+     * Marks a single class as visible.
+     *
+     * @param clazz the class
+     */
+    public void allowClass(Class<?> clazz) {
+        classNames.add(clazz.getName());
+    }
+
+    /**
+     * Marks a single class as not visible.
+     *
+     * @param className the class name
+     */
+    public void disallowClass(String className) {
+        disallowedClassNames.add(className);
+    }
+
+    /**
+     * Marks all resources with the given prefix as visible.
+     *
+     * @param resourcePrefix the resource prefix
+     */
+    public void allowResources(String resourcePrefix) {
+        resourcePrefixes.add(resourcePrefix + "/");
+    }
+
+    /**
+     * Marks a single resource as visible.
+     *
+     * @param resourceName the resource name
+     */
+    public void allowResource(String resourceName) {
+        resourceNames.add(resourceName);
+    }
+
+    public static class Spec extends ClassLoaderSpec {
+        final Set<String> packageNames;
+        final Set<String> packagePrefixes;
+        final Set<String> resourcePrefixes;
+        final Set<String> resourceNames;
+        final Set<String> classNames;
+        final Set<String> disallowedClassNames;
+
+        public Spec(Set<String> classNames, Set<String> packageNames, Set<String> packagePrefixes, Set<String> resourcePrefixes, Set<String> resourceNames, Set<String> disallowedClassNames) {
+            this.classNames = new HashSet<String>(classNames);
+            this.packageNames = new HashSet<String>(packageNames);
+            this.packagePrefixes = new HashSet<String>(packagePrefixes);
+            this.resourcePrefixes = new HashSet<String>(resourcePrefixes);
+            this.resourceNames = new HashSet<String>(resourceNames);
+            this.disallowedClassNames = new HashSet<String>(disallowedClassNames);
+        }
+
+        @Override
+        public boolean equals(Object obj) {
+            if (obj == this) {
+                return true;
+            }
+            if (obj == null || obj.getClass() != getClass()) {
+                return false;
+            }
+            Spec other = (Spec) obj;
+            return other.packageNames.equals(packageNames)
+                    && other.packagePrefixes.equals(packagePrefixes)
+                    && other.resourceNames.equals(resourceNames)
+                    && other.resourcePrefixes.equals(resourcePrefixes)
+                    && other.classNames.equals(classNames)
+                    && other.disallowedClassNames.equals(disallowedClassNames);
+        }
+
+        @Override
+        public int hashCode() {
+            return packageNames.hashCode()
+                    ^ packagePrefixes.hashCode()
+                    ^ resourceNames.hashCode()
+                    ^ resourcePrefixes.hashCode()
+                    ^ classNames.hashCode()
+                    ^ disallowedClassNames.hashCode();
+        }
+    }
+}
diff --git a/subprojects/base-services/src/main/java/org/gradle/internal/classloader/MultiParentClassLoader.java b/subprojects/base-services/src/main/java/org/gradle/internal/classloader/MultiParentClassLoader.java
new file mode 100644
index 0000000..8406559
--- /dev/null
+++ b/subprojects/base-services/src/main/java/org/gradle/internal/classloader/MultiParentClassLoader.java
@@ -0,0 +1,126 @@
+/*
+ * Copyright 2010 the original author or authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.gradle.internal.classloader;
+
+import org.gradle.internal.reflect.JavaReflectionUtil;
+import org.gradle.util.JavaMethod;
+
+import java.io.IOException;
+import java.net.URL;
+import java.util.*;
+import java.util.concurrent.CopyOnWriteArrayList;
+
+/**
+ * A {@code ClassLoader} which delegates to multiple parent ClassLoaders.
+ *
+ * Note: It's usually a good idea to add a {@link CachingClassLoader} between this ClassLoader and any
+ * ClassLoaders that use it as a parent, to prevent every path in the ClassLoader graph being searched.
+ */
+public class MultiParentClassLoader extends ClassLoader implements ClassLoaderHierarchy {
+    private final List<ClassLoader> parents;
+    private final JavaMethod<ClassLoader, Package[]> getPackagesMethod;
+    private final JavaMethod<ClassLoader, Package> getPackageMethod;
+
+    public MultiParentClassLoader(ClassLoader... parents) {
+        this(Arrays.asList(parents));
+    }
+
+    public MultiParentClassLoader(Collection<? extends ClassLoader> parents) {
+        super(null);
+        this.parents = new CopyOnWriteArrayList<ClassLoader>(parents);
+        getPackagesMethod = JavaReflectionUtil.method(ClassLoader.class, Package[].class, "getPackages");
+        getPackageMethod = JavaReflectionUtil.method(ClassLoader.class, Package.class, "getPackage", String.class);
+    }
+
+    public void addParent(ClassLoader parent) {
+        parents.add(parent);
+    }
+
+    public void visit(ClassLoaderVisitor visitor) {
+        visitor.visitSpec(new Spec());
+        for (ClassLoader parent : parents) {
+            visitor.visitParent(parent);
+        }
+    }
+
+    @Override
+    protected Class<?> loadClass(String name, boolean resolve) throws ClassNotFoundException {
+        for (ClassLoader parent : parents) {
+            try {
+                return parent.loadClass(name);
+            } catch (ClassNotFoundException e) {
+                // Expected
+            }
+        }
+        throw new ClassNotFoundException(String.format("%s not found.", name));
+    }
+
+    @Override
+    protected Package getPackage(String name) {
+        for (ClassLoader parent : parents) {
+            Package p = getPackageMethod.invoke(parent, name);
+            if (p != null) {
+                return p;
+            }
+        }
+        return null;
+    }
+
+    @Override
+    protected Package[] getPackages() {
+        Set<Package> packages = new LinkedHashSet<Package>();
+        for (ClassLoader parent : parents) {
+            Package[] parentPackages = getPackagesMethod.invoke(parent);
+            packages.addAll(Arrays.asList(parentPackages));
+        }
+        return packages.toArray(new Package[packages.size()]);
+    }
+
+    @Override
+    public URL getResource(String name) {
+        for (ClassLoader parent : parents) {
+            URL resource = parent.getResource(name);
+            if (resource != null) {
+                return resource;
+            }
+        }
+        return null;
+    }
+
+    @Override
+    public Enumeration<URL> getResources(String name) throws IOException {
+        Set<URL> resources = new LinkedHashSet<URL>();
+        for (ClassLoader parent : parents) {
+            Enumeration<URL> parentResources = parent.getResources(name);
+            while (parentResources.hasMoreElements()) {
+                resources.add(parentResources.nextElement());
+            }
+        }
+        return Collections.enumeration(resources);
+    }
+
+    public static class Spec extends ClassLoaderSpec {
+        @Override
+        public boolean equals(Object obj) {
+            return obj != null && obj.getClass().equals(Spec.class);
+        }
+
+        @Override
+        public int hashCode() {
+            return getClass().getName().hashCode();
+        }
+    }
+}
diff --git a/subprojects/base-services/src/main/java/org/gradle/internal/classloader/MutableURLClassLoader.java b/subprojects/base-services/src/main/java/org/gradle/internal/classloader/MutableURLClassLoader.java
new file mode 100755
index 0000000..4f5f6ea
--- /dev/null
+++ b/subprojects/base-services/src/main/java/org/gradle/internal/classloader/MutableURLClassLoader.java
@@ -0,0 +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.internal.classloader;
+
+import org.gradle.internal.classpath.ClassPath;
+import org.gradle.util.CollectionUtils;
+
+import java.net.URL;
+import java.net.URLClassLoader;
+import java.util.Collection;
+import java.util.List;
+
+public class MutableURLClassLoader extends URLClassLoader implements ClassLoaderHierarchy {
+    public MutableURLClassLoader(ClassLoader parent, URL... urls) {
+        super(urls, parent);
+    }
+
+    public MutableURLClassLoader(ClassLoader parent, Collection<URL> urls) {
+        super(urls.toArray(new URL[urls.size()]), parent);
+    }
+
+    public MutableURLClassLoader(ClassLoader parent, ClassPath classPath) {
+        super(classPath.getAsURLArray(), parent);
+    }
+
+    public MutableURLClassLoader(ClassLoader parent, Spec spec) {
+        this(parent, spec.classpath);
+    }
+
+    public void visit(ClassLoaderVisitor visitor) {
+        visitor.visitSpec(new Spec(CollectionUtils.toList(getURLs())));
+        visitor.visitClassPath(getURLs());
+        visitor.visitParent(getParent());
+    }
+
+    @Override
+    public void addURL(URL url) {
+        super.addURL(url);
+    }
+
+    public void addURLs(Iterable<URL> urls) {
+        for (URL url : urls) {
+            addURL(url);
+        }
+    }
+
+    public static class Spec extends ClassLoaderSpec {
+        final List<URL> classpath;
+
+        public Spec(List<URL> classpath) {
+            this.classpath = classpath;
+        }
+
+        public List<URL> getClasspath() {
+            return classpath;
+        }
+
+        @Override
+        public boolean equals(Object obj) {
+            if (obj == this) {
+                return true;
+            }
+            if (obj == null || obj.getClass() != getClass()) {
+                return false;
+            }
+            Spec other = (Spec) obj;
+            return classpath.equals(other.classpath);
+        }
+
+        @Override
+        public int hashCode() {
+            return classpath.hashCode();
+        }
+    }
+}
diff --git a/subprojects/base-services/src/main/java/org/gradle/internal/classloader/TransformingClassLoader.java b/subprojects/base-services/src/main/java/org/gradle/internal/classloader/TransformingClassLoader.java
new file mode 100644
index 0000000..41c4a8d
--- /dev/null
+++ b/subprojects/base-services/src/main/java/org/gradle/internal/classloader/TransformingClassLoader.java
@@ -0,0 +1,63 @@
+/*
+ * Copyright 2013 the original author or authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.gradle.internal.classloader;
+
+import com.google.common.io.ByteStreams;
+import org.gradle.api.GradleException;
+import org.gradle.internal.classpath.ClassPath;
+
+import java.io.IOException;
+import java.io.InputStream;
+import java.net.URL;
+import java.util.Collection;
+
+public abstract class TransformingClassLoader extends MutableURLClassLoader {
+    public TransformingClassLoader(ClassLoader parent, ClassPath classPath) {
+        super(parent, classPath);
+    }
+
+    public TransformingClassLoader(ClassLoader parent, Collection<URL> urls) {
+        super(parent, urls);
+    }
+
+    @Override
+    protected Class<?> findClass(String name) throws ClassNotFoundException {
+        URL resource = findResource(name.replace(".", "/") + ".class");
+        if (resource == null) {
+            throw new ClassNotFoundException(name);
+        }
+        byte[] bytes;
+        try {
+            bytes = loadBytecode(resource);
+            bytes = transform(bytes);
+        } catch (Exception e) {
+            throw new GradleException(String.format("Could not load class '%s' from %s.", name, resource), e);
+        }
+        return defineClass(name, bytes, 0, bytes.length);
+    }
+
+    private byte[] loadBytecode(URL resource) throws IOException {
+        InputStream inputStream = resource.openStream();
+        try {
+            return ByteStreams.toByteArray(inputStream);
+        } finally {
+            inputStream.close();
+        }
+    }
+
+    protected abstract byte[] transform(byte[] bytes);
+}
diff --git a/subprojects/base-services/src/main/java/org/gradle/internal/concurrent/ServiceLifecycle.java b/subprojects/base-services/src/main/java/org/gradle/internal/concurrent/ServiceLifecycle.java
new file mode 100644
index 0000000..98642c0
--- /dev/null
+++ b/subprojects/base-services/src/main/java/org/gradle/internal/concurrent/ServiceLifecycle.java
@@ -0,0 +1,118 @@
+/*
+ * Copyright 2013 the original author or authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.gradle.internal.concurrent;
+
+import org.gradle.internal.UncheckedException;
+
+import java.util.HashMap;
+import java.util.Map;
+import java.util.concurrent.locks.Condition;
+import java.util.concurrent.locks.Lock;
+import java.util.concurrent.locks.ReentrantLock;
+
+/**
+ * Manages the lifecycle of some thread-safe service or resource.
+ */
+public class ServiceLifecycle implements AsyncStoppable {
+    private enum State {RUNNING, STOPPING, STOPPED}
+
+    private final String displayName;
+    private final Lock lock = new ReentrantLock();
+    private final Condition condition = lock.newCondition();
+    private State state = State.RUNNING;
+    private Map<Thread, Integer> usages = new HashMap<Thread, Integer>();
+
+    public ServiceLifecycle(String displayName) {
+        this.displayName = displayName;
+    }
+
+    public void use(Runnable runnable) {
+        lock.lock();
+        try {
+            switch (state) {
+                case STOPPING:
+                    throw new IllegalStateException(String.format("Cannot use %s as it is currently stopping.", displayName));
+                case STOPPED:
+                    throw new IllegalStateException(String.format("Cannot use %s as it has been stopped.", displayName));
+            }
+            Integer depth = usages.get(Thread.currentThread());
+            if (depth == null) {
+                usages.put(Thread.currentThread(), 1);
+            } else {
+                usages.put(Thread.currentThread(), depth + 1);
+            }
+        } finally {
+            lock.unlock();
+        }
+
+        try {
+            runnable.run();
+        } finally {
+            lock.lock();
+            try {
+                Integer depth = usages.remove(Thread.currentThread());
+                if (depth > 1) {
+                    usages.put(Thread.currentThread(), depth - 1);
+                }
+                if (usages.isEmpty()) {
+                    condition.signalAll();
+                    if (state == State.STOPPING) {
+                        state = State.STOPPED;
+                    }
+                }
+            } finally {
+                lock.unlock();
+            }
+        }
+    }
+
+    public void requestStop() {
+        lock.lock();
+        try {
+            if (state == State.RUNNING) {
+                if (usages.isEmpty()) {
+                    state = State.STOPPED;
+                } else {
+                    state = State.STOPPING;
+                }
+            }
+        } finally {
+            lock.unlock();
+        }
+    }
+
+    public void stop() {
+        lock.lock();
+        try {
+            if (usages.containsKey(Thread.currentThread())) {
+                throw new IllegalStateException(String.format("Cannot stop %s from a thread that is using it.", displayName));
+            }
+            while (!usages.isEmpty()) {
+                try {
+                    condition.await();
+                } catch (InterruptedException e) {
+                    throw UncheckedException.throwAsUncheckedException(e);
+                }
+            }
+            if (state != State.STOPPED) {
+                state = State.STOPPED;
+            }
+        } finally {
+            lock.unlock();
+        }
+    }
+}
diff --git a/subprojects/base-services/src/main/java/org/gradle/internal/jvm/JavaHomeException.java b/subprojects/base-services/src/main/java/org/gradle/internal/jvm/JavaHomeException.java
index 92335e4..57e9dda 100644
--- a/subprojects/base-services/src/main/java/org/gradle/internal/jvm/JavaHomeException.java
+++ b/subprojects/base-services/src/main/java/org/gradle/internal/jvm/JavaHomeException.java
@@ -16,9 +16,6 @@
 
 package org.gradle.internal.jvm;
 
-/**
- * by Szczepan Faber, created at: 1/23/12
- */
 public class JavaHomeException extends RuntimeException {
     public JavaHomeException(String message) {
         super(message);
diff --git a/subprojects/base-services/src/main/java/org/gradle/internal/jvm/JavaInfo.java b/subprojects/base-services/src/main/java/org/gradle/internal/jvm/JavaInfo.java
index cb4e842..0967b36 100644
--- a/subprojects/base-services/src/main/java/org/gradle/internal/jvm/JavaInfo.java
+++ b/subprojects/base-services/src/main/java/org/gradle/internal/jvm/JavaInfo.java
@@ -19,9 +19,6 @@ package org.gradle.internal.jvm;
 import java.io.File;
 import java.util.Map;
 
-/**
- * by Szczepan Faber, created at: 2/6/12
- */
 public interface JavaInfo {
     /**
      * @return the executable
diff --git a/subprojects/base-services/src/main/java/org/gradle/internal/jvm/Jre.java b/subprojects/base-services/src/main/java/org/gradle/internal/jvm/Jre.java
new file mode 100644
index 0000000..af50c3c
--- /dev/null
+++ b/subprojects/base-services/src/main/java/org/gradle/internal/jvm/Jre.java
@@ -0,0 +1,26 @@
+/*
+ * Copyright 2013 the original author or authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.gradle.internal.jvm;
+
+import java.io.File;
+
+/**
+ * Represents a JRE installation.
+ */
+public abstract class Jre {
+    public abstract File getHomeDir();
+}
diff --git a/subprojects/base-services/src/main/java/org/gradle/internal/jvm/Jvm.java b/subprojects/base-services/src/main/java/org/gradle/internal/jvm/Jvm.java
index db4b1f9..bbca272 100644
--- a/subprojects/base-services/src/main/java/org/gradle/internal/jvm/Jvm.java
+++ b/subprojects/base-services/src/main/java/org/gradle/internal/jvm/Jvm.java
@@ -17,6 +17,7 @@
 package org.gradle.internal.jvm;
 
 import org.gradle.api.JavaVersion;
+import org.gradle.api.Nullable;
 import org.gradle.internal.SystemProperties;
 import org.gradle.internal.UncheckedException;
 import org.gradle.internal.os.OperatingSystem;
@@ -27,6 +28,7 @@ import java.io.File;
 import java.io.IOException;
 import java.util.HashMap;
 import java.util.Map;
+import java.util.concurrent.atomic.AtomicReference;
 
 public class Jvm implements JavaInfo {
     
@@ -39,12 +41,18 @@ public class Jvm implements JavaInfo {
     private final File javaHome;
     private final boolean userSupplied;
     private final JavaVersion javaVersion;
+    private static final AtomicReference<Jvm> CURRENT = new AtomicReference<Jvm>();
 
     public static Jvm current() {
-        return create(null);
+        Jvm jvm = CURRENT.get();
+        if (jvm == null) {
+            CURRENT.compareAndSet(null, create(null));
+            jvm = CURRENT.get();
+        }
+        return jvm;
     }
 
-    private static Jvm create(File javaBase) {
+    static Jvm create(File javaBase) {
         String vendor = System.getProperty("java.vm.vendor");
         if (vendor.toLowerCase().startsWith("apple inc.")) {
             return new AppleJvm(OperatingSystem.current(), javaBase);
@@ -143,6 +151,10 @@ public class Jvm implements JavaInfo {
         return findExecutable("java");
     }
 
+    public File getJavacExecutable() throws JavaHomeException {
+        return findExecutable("javac");
+    }
+
     /**
      * {@inheritDoc}
      */
@@ -187,6 +199,10 @@ public class Jvm implements JavaInfo {
      */
     public File getRuntimeJar() {
         File runtimeJar = new File(javaBase, "lib/rt.jar");
+        if (runtimeJar.exists()) {
+            return runtimeJar;
+        }
+        runtimeJar = new File(javaBase, "jre/lib/rt.jar");
         return runtimeJar.exists() ? runtimeJar : null;
     }
 
@@ -197,6 +213,39 @@ public class Jvm implements JavaInfo {
         return findToolsJar(javaBase);
     }
 
+    /**
+     * Locates a stand-alone JRE installation for this JVM. Returns null if not found.
+     */
+    @Nullable
+    public Jre getStandaloneJre() {
+        if (os.isWindows()) {
+            File jreDir;
+            if (javaVersion.isJava5()) {
+                jreDir = new File(javaHome.getParentFile(), String.format("jre%s", SystemProperties.getJavaVersion()));
+            } else {
+                jreDir = new File(javaHome.getParentFile(), String.format("jre%s", javaVersion.getMajorVersion()));
+            }
+            if (jreDir.isDirectory()) {
+                return new DefaultJre(jreDir);
+            }
+        }
+        if (!new File(javaHome, "jre").isDirectory()) {
+            return new DefaultJre(javaHome);
+        }
+        return null;
+    }
+
+    /**
+     * Locates the JRE installation for this JVM.
+     */
+    public Jre getJre() {
+        File jreDir = new File(javaBase, "jre");
+        if (jreDir.isDirectory()) {
+            return new DefaultJre(jreDir);
+        }
+        return new DefaultJre(javaBase);
+    }
+
     private File findToolsJar(File javaHome) {
         File toolsJar = new File(javaHome, "lib/tools.jar");
         if (toolsJar.exists()) {
@@ -209,11 +258,15 @@ public class Jvm implements JavaInfo {
                 return toolsJar;
             }
         }
-        if (javaHome.getName().matches("jre\\d+") && os.isWindows()) {
-            javaHome = new File(javaHome.getParentFile(), String.format("jdk%s", SystemProperties.getJavaVersion()));
-            toolsJar = new File(javaHome, "lib/tools.jar");
-            if (toolsJar.exists()) {
-                return toolsJar;
+
+        if (os.isWindows()) {
+            String version = SystemProperties.getJavaVersion();
+            if (javaHome.getName().matches("jre\\d+") || javaHome.getName().equals(String.format("jre%s", version))) {
+                javaHome = new File(javaHome.getParentFile(), String.format("jdk%s", version));
+                toolsJar = new File(javaHome, "lib/tools.jar");
+                if (toolsJar.exists()) {
+                    return toolsJar;
+                }
             }
         }
 
@@ -288,4 +341,17 @@ public class Jvm implements JavaInfo {
             return vars;
         }
     }
+
+    private static class DefaultJre extends Jre {
+        private final File jreDir;
+
+        public DefaultJre(File jreDir) {
+            this.jreDir = jreDir;
+        }
+
+        @Override
+        public File getHomeDir() {
+            return jreDir;
+        }
+    }
 }
diff --git a/subprojects/base-services/src/main/java/org/gradle/internal/os/OperatingSystem.java b/subprojects/base-services/src/main/java/org/gradle/internal/os/OperatingSystem.java
index 08dd03d..cb87123 100755
--- a/subprojects/base-services/src/main/java/org/gradle/internal/os/OperatingSystem.java
+++ b/subprojects/base-services/src/main/java/org/gradle/internal/os/OperatingSystem.java
@@ -15,6 +15,8 @@
  */
 package org.gradle.internal.os;
 
+import org.gradle.api.Nullable;
+
 import java.io.File;
 import java.util.ArrayList;
 import java.util.Collections;
@@ -82,9 +84,12 @@ public abstract class OperatingSystem {
 
     public abstract String getSharedLibraryName(String libraryName);
 
+    public abstract String getStaticLibraryName(String libraryName);
+
     /**
      * Locates the given executable in the system path. Returns null if not found.
      */
+    @Nullable
     public File findInPath(String name) {
         String exeName = getExecutableName(name);
         if (exeName.contains(File.separator)) {
@@ -117,8 +122,8 @@ public abstract class OperatingSystem {
         return all;
     }
     
-    List<File> getPath() {                       
-        String path = System.getenv("PATH");
+    List<File> getPath() {
+        String path = System.getenv(getPathVar());
         if (path == null) {
             return Collections.emptyList();
         }
@@ -129,6 +134,10 @@ public abstract class OperatingSystem {
         return entries;
     }
 
+    public String getPathVar() {
+        return "PATH";
+    }
+
     static class Windows extends OperatingSystem {
         @Override
         public boolean isWindows() {
@@ -137,10 +146,7 @@ public abstract class OperatingSystem {
 
         @Override
         public String getScriptName(String scriptPath) {
-            if (scriptPath.toLowerCase().endsWith(".bat")) {
-                return scriptPath;
-            }
-            return scriptPath + ".bat";
+            return withSuffix(scriptPath, ".bat");
         }
 
         @Override
@@ -154,6 +160,11 @@ public abstract class OperatingSystem {
         }
 
         @Override
+        public String getStaticLibraryName(String libraryName) {
+            return withSuffix(libraryName, ".lib");
+        }
+
+        @Override
         public String getNativePrefix() {
             String arch = System.getProperty("os.arch");
             if ("i386".equals(arch)) {
@@ -166,7 +177,23 @@ public abstract class OperatingSystem {
             if (executablePath.toLowerCase().endsWith(extension)) {
                 return executablePath;
             }
-            return executablePath + extension;
+            return removeExtension(executablePath) + extension;
+        }
+
+        private String removeExtension(String executablePath) {
+            int fileNameStart = Math.max(executablePath.lastIndexOf('/'), executablePath.lastIndexOf('\\'));
+            int extensionPos = executablePath.lastIndexOf('.');
+
+            if (extensionPos > fileNameStart) {
+                return executablePath.substring(0, extensionPos);
+            }
+            return executablePath;
+        }
+
+
+        @Override
+        public String getPathVar() {
+            return "Path";
         }
     }
 
@@ -183,7 +210,10 @@ public abstract class OperatingSystem {
 
         @Override
         public String getSharedLibraryName(String libraryName) {
-            String suffix = getSharedLibSuffix();
+            return getLibraryName(libraryName, getSharedLibSuffix());
+        }
+
+        private String getLibraryName(String libraryName, String suffix) {
             if (libraryName.endsWith(suffix)) {
                 return libraryName;
             }
@@ -200,6 +230,11 @@ public abstract class OperatingSystem {
         }
 
         @Override
+        public String getStaticLibraryName(String libraryName) {
+            return getLibraryName(libraryName, ".a");
+        }
+
+        @Override
         public boolean isUnix() {
             return true;
         }
diff --git a/subprojects/base-services/src/main/java/org/gradle/internal/reflect/DirectInstantiator.java b/subprojects/base-services/src/main/java/org/gradle/internal/reflect/DirectInstantiator.java
index 65a2ac0..cd7f87f 100644
--- a/subprojects/base-services/src/main/java/org/gradle/internal/reflect/DirectInstantiator.java
+++ b/subprojects/base-services/src/main/java/org/gradle/internal/reflect/DirectInstantiator.java
@@ -22,7 +22,7 @@ import java.util.Arrays;
 import java.util.List;
 
 public class DirectInstantiator implements Instantiator {
-    public <T> T newInstance(Class<T> type, Object... params) {
+    public <T> T newInstance(Class<? extends T> type, Object... params) {
         try {
             List<Constructor<?>> matches = new ArrayList<Constructor<?>>();
             for (Constructor<?> constructor : type.getConstructors()) {
diff --git a/subprojects/base-services/src/main/java/org/gradle/internal/reflect/Instantiator.java b/subprojects/base-services/src/main/java/org/gradle/internal/reflect/Instantiator.java
index 6c50e84..a131ccd 100644
--- a/subprojects/base-services/src/main/java/org/gradle/internal/reflect/Instantiator.java
+++ b/subprojects/base-services/src/main/java/org/gradle/internal/reflect/Instantiator.java
@@ -25,6 +25,6 @@ public interface Instantiator {
      *
      * @throws ObjectInstantiationException On failure to create the new instance.
      */
-    <T> T newInstance(Class<T> type, Object... parameters) throws ObjectInstantiationException;
+    <T> T newInstance(Class<? extends T> type, Object... parameters) throws ObjectInstantiationException;
 
 }
\ No newline at end of file
diff --git a/subprojects/base-services/src/main/java/org/gradle/internal/reflect/JavaReflectionUtil.java b/subprojects/base-services/src/main/java/org/gradle/internal/reflect/JavaReflectionUtil.java
index 215786c..b6bce36 100644
--- a/subprojects/base-services/src/main/java/org/gradle/internal/reflect/JavaReflectionUtil.java
+++ b/subprojects/base-services/src/main/java/org/gradle/internal/reflect/JavaReflectionUtil.java
@@ -16,46 +16,134 @@
 
 package org.gradle.internal.reflect;
 
+import org.gradle.api.Transformer;
+import org.gradle.api.specs.Spec;
 import org.gradle.internal.UncheckedException;
+import org.gradle.util.CollectionUtils;
+import org.gradle.util.JavaMethod;
 
+import java.lang.annotation.Annotation;
+import java.lang.annotation.Inherited;
+import java.lang.reflect.Field;
+import java.lang.reflect.InvocationTargetException;
 import java.lang.reflect.Method;
+import java.lang.reflect.Modifier;
+import java.util.*;
 
-/**
- * Simple implementations of some reflection capabilities. In contrast to org.gradle.util.ReflectionUtil,
- * this class doesn't make use of Groovy.
- */
 public class JavaReflectionUtil {
-    public static Object readProperty(Object target, String property) {
-        try {
-            Method getterMethod;
-            try {
-                getterMethod = target.getClass().getMethod(toMethodName("get", property));
-            } catch (NoSuchMethodException e) {
-                try {
-                    getterMethod = target.getClass().getMethod(toMethodName("is", property));
-                } catch (NoSuchMethodException e2) {
-                    throw e;
-                }
+    /**
+     * Locates the readable properties of the given type. Searches only public properties.
+     */
+    public static Map<String, PropertyAccessor> readableProperties(Class<?> target) {
+        HashMap<String, PropertyAccessor> properties = new HashMap<String, PropertyAccessor>();
+        for (Method method : target.getMethods()) {
+            if (method.getName().startsWith("get") && isGetter(method)) {
+                String propertyName = method.getName().substring(3);
+                propertyName = Character.toLowerCase(propertyName.charAt(0)) + propertyName.substring(1);
+                properties.put(propertyName, new GetterMethodBackedPropertyAccessor(propertyName, method));
+            } else if (method.getName().startsWith("is") && isBooleanGetter(method)) {
+                String propertyName = method.getName().substring(2);
+                propertyName = Character.toLowerCase(propertyName.charAt(0)) + propertyName.substring(1);
+                properties.put(propertyName, new GetterMethodBackedPropertyAccessor(propertyName, method));
             }
-            return getterMethod.invoke(target);
-        } catch (Exception e) {
-            throw UncheckedException.throwAsUncheckedException(e);
         }
+        return properties;
+    }
+
+    /**
+     * Locates the property with the given name as a readable property. Searches only public properties.
+     *
+     * @throws NoSuchPropertyException when the given property does not exist.
+     */
+    public static PropertyAccessor readableProperty(Class<?> target, String property) {
+        final Method getterMethod = findGetterMethod(target, property);
+        if (getterMethod == null) {
+            throw new NoSuchPropertyException(String.format("Could not find getter method for property '%s' on class %s.", property, target.getSimpleName()));
+        }
+        return new GetterMethodBackedPropertyAccessor(property, getterMethod);
     }
 
-    public static void writeProperty(Object target, String property, Object value) {
+    private static Method findGetterMethod(Class<?> target, String property) {
         try {
-            String setterName = toMethodName("set", property);
-            for (Method method: target.getClass().getMethods()) {
-                if (!method.getName().equals(setterName)) { continue; }
-                if (method.getParameterTypes().length != 1) { continue; }
-                method.invoke(target, value);
-                return;
+            Method getterMethod = target.getMethod(toMethodName("get", property));
+            if (isGetter(getterMethod)) {
+                return getterMethod;
+            }
+        } catch (NoSuchMethodException e) {
+            // Ignore
+        }
+        try {
+            Method getterMethod = target.getMethod(toMethodName("is", property));
+            if (isBooleanGetter(getterMethod)) {
+                return getterMethod;
+            }
+        } catch (NoSuchMethodException e2) {
+            // Ignore
+        }
+        return null;
+    }
+
+    private static boolean isGetter(Method method) {
+        return method.getParameterTypes().length == 0 && !Modifier.isStatic(method.getModifiers()) && !method.getReturnType().equals(Void.TYPE);
+    }
+
+    private static boolean isBooleanGetter(Method method) {
+        Class<?> returnType = method.getReturnType();
+        return method.getParameterTypes().length == 0 && !Modifier.isStatic(method.getModifiers()) && (returnType.equals(Boolean.TYPE) || returnType.equals(Boolean.class));
+    }
+
+    /**
+     * Locates the property with the given name as a writable property. Searches only public properties.
+     *
+     * @throws NoSuchPropertyException when the given property does not exist.
+     */
+    public static PropertyMutator writeableProperty(Class<?> target, String property) {
+        String setterName = toMethodName("set", property);
+        for (final Method method : target.getMethods()) {
+            if (!method.getName().equals(setterName)) {
+                continue;
+            }
+            if (method.getParameterTypes().length != 1) {
+                continue;
+            }
+            if (Modifier.isStatic(method.getModifiers())) {
+                continue;
+            }
+            return new MethodBackedPropertyMutator(property, method);
+        }
+        throw new NoSuchPropertyException(String.format("Could not find setter method for property '%s' on class %s.", property, target.getSimpleName()));
+    }
+
+    public static <T> T readField(Object target, Class<T> type, String name) {
+        Class<?> objectType = target.getClass();
+        while (objectType != null) {
+            try {
+                Field field = objectType.getDeclaredField(name);
+                if (type.isAssignableFrom(field.getType())) {
+                    field.setAccessible(true);
+                    Object value;
+                    try {
+                        value = field.get(target);
+                    } catch (IllegalAccessException e) {
+                        throw UncheckedException.throwAsUncheckedException(e);
+                    }
+
+                    if (type.isPrimitive()) {
+                        @SuppressWarnings("unchecked")
+                        T cast = (T) getWrapperTypeForPrimitiveType(type).cast(value);
+                        return cast;
+                    } else {
+                        return type.cast(value);
+                    }
+                }
+            } catch (NoSuchFieldException ignore) {
+                // ignore
             }
-            throw new NoSuchMethodException(String.format("could not find setter method '%s'", setterName));
-        } catch (Exception e) {
-            throw UncheckedException.throwAsUncheckedException(e);
+
+            objectType = objectType.getSuperclass();
         }
+
+        throw UncheckedException.throwAsUncheckedException(new NoSuchFieldException("Could not find field '" + name + "' with type '" + type.getClass() + "' on class '" + target.getClass() + "'"));
     }
 
     private static String toMethodName(String prefix, String propertyName) {
@@ -80,4 +168,206 @@ public class JavaReflectionUtil {
         }
         throw new IllegalArgumentException(String.format("Don't know how wrapper type for primitive type %s.", type));
     }
+
+    public static <T, R> JavaMethod<T, R> method(Class<T> target, Class<R> returnType, String name, Class<?>... paramTypes) {
+        return new JavaMethod<T, R>(target, returnType, name, paramTypes);
+    }
+
+    public static <T, R> JavaMethod<T, R> method(T target, Class<R> returnType, String name, Class<?>... paramTypes) {
+        @SuppressWarnings("unchecked")
+        Class<T> targetClass = (Class<T>) target.getClass();
+        return method(targetClass, returnType, name, paramTypes);
+    }
+
+    public static <T, R> JavaMethod<T, R> method(Class<T> target, Class<R> returnType, Method method) {
+        return new JavaMethod<T, R>(target, returnType, method);
+    }
+
+    public static <T, R> JavaMethod<T, R> method(T target, Class<R> returnType, Method method) {
+        @SuppressWarnings("unchecked")
+        Class<T> targetClass = (Class<T>) target.getClass();
+        return new JavaMethod<T, R>(targetClass, returnType, method);
+    }
+
+    /**
+     * Search methods in an inheritance aware fashion, stopping when stopIndicator returns true.
+     */
+    public static void searchMethods(Class<?> target, final Transformer<Boolean, Method> stopIndicator) {
+        Spec<Method> stopIndicatorAsSpec = new Spec<Method>() {
+            public boolean isSatisfiedBy(Method element) {
+                return stopIndicator.transform(element);
+            }
+        };
+
+        findAllMethodsInternal(target, stopIndicatorAsSpec, new MultiMap<String, Method>(), new ArrayList<Method>(1), true);
+    }
+
+    public static Method findMethod(Class<?> target, Spec<Method> predicate) {
+        List<Method> methods = findAllMethodsInternal(target, predicate, new MultiMap<String, Method>(), new ArrayList<Method>(1), true);
+        return methods.isEmpty() ? null : methods.get(0);
+    }
+
+    // Not hasProperty() because that's awkward with Groovy objects implementing it
+    public static boolean propertyExists(Object target, String propertyName) {
+        Class<?> targetType = target.getClass();
+        Method getterMethod = findGetterMethod(target.getClass(), propertyName);
+        if (getterMethod == null) {
+            try {
+                Field field = targetType.getField(propertyName);
+                return true;
+            } catch (NoSuchFieldException ignore) {
+                // ignore
+            }
+        } else {
+            return true;
+        }
+
+        return false;
+    }
+
+    private static class MultiMap<K, V> extends HashMap<K, List<V>> {
+        @Override
+        public List<V> get(Object key) {
+            if (!containsKey(key)) {
+                @SuppressWarnings("unchecked") K keyCast = (K) key;
+                put(keyCast, new LinkedList<V>());
+            }
+
+            return super.get(key);
+        }
+    }
+
+    private static List<Method> findAllMethodsInternal(Class<?> target, Spec<Method> predicate, MultiMap<String, Method> seen, List<Method> collector, boolean stopAtFirst) {
+        for (final Method method : target.getDeclaredMethods()) {
+            List<Method> seenWithName = seen.get(method.getName());
+            Method override = CollectionUtils.findFirst(seenWithName, new Spec<Method>() {
+                public boolean isSatisfiedBy(Method potentionOverride) {
+                    return potentionOverride.getName().equals(method.getName())
+                            && Arrays.equals(potentionOverride.getParameterTypes(), method.getParameterTypes());
+                }
+            });
+
+
+            if (override == null) {
+                seenWithName.add(method);
+                if (predicate.isSatisfiedBy(method)) {
+                    collector.add(method);
+                    if (stopAtFirst) {
+                        return collector;
+                    }
+                }
+            }
+        }
+
+        Class<?> parent = target.getSuperclass();
+        if (parent != null) {
+            return findAllMethodsInternal(parent, predicate, seen, collector, stopAtFirst);
+        }
+
+        return collector;
+    }
+
+    public static <A extends Annotation> A getAnnotation(Class<?> type, Class<A> annotationType) {
+        return getAnnotation(type, annotationType, true);
+    }
+
+    private static <A extends Annotation> A getAnnotation(Class<?> type, Class<A> annotationType, boolean checkType) {
+        A annotation;
+        if (checkType) {
+            annotation = type.getAnnotation(annotationType);
+            if (annotation != null) {
+                return annotation;
+            }
+        }
+
+        if (annotationType.getAnnotation(Inherited.class) != null) {
+            for (Class<?> anInterface : type.getInterfaces()) {
+                annotation = getAnnotation((Class<?>) anInterface, (Class<A>) annotationType, true);
+                if (annotation != null) {
+                    return annotation;
+                }
+            }
+        }
+
+        if (type.isInterface() || type.equals(Object.class)) {
+            return null;
+        } else {
+            return getAnnotation((Class<?>) type.getSuperclass(), (Class<A>) annotationType, false);
+        }
+    }
+
+    public static boolean isClassAvailable(String className) {
+        try {
+            JavaReflectionUtil.class.getClassLoader().loadClass(className);
+            return true;
+        } catch (ClassNotFoundException e) {
+            return false;
+        }
+    }
+
+    private static class GetterMethodBackedPropertyAccessor implements PropertyAccessor {
+        private final String property;
+        private final Method method;
+
+        public GetterMethodBackedPropertyAccessor(String property, Method method) {
+            this.property = property;
+            this.method = method;
+        }
+
+        @Override
+        public String toString() {
+            return String.format("property %s.%s", method.getDeclaringClass().getSimpleName(), property);
+        }
+
+        public String getName() {
+            return property;
+        }
+
+        public Class<?> getType() {
+            return method.getClass();
+        }
+
+        public Object getValue(Object target) {
+            try {
+                return method.invoke(target);
+            } catch (InvocationTargetException e) {
+                throw UncheckedException.unwrapAndRethrow(e);
+            } catch (Exception e) {
+                throw UncheckedException.throwAsUncheckedException(e);
+            }
+        }
+    }
+
+    private static class MethodBackedPropertyMutator implements PropertyMutator {
+        private final String property;
+        private final Method method;
+
+        public MethodBackedPropertyMutator(String property, Method method) {
+            this.property = property;
+            this.method = method;
+        }
+
+        @Override
+        public String toString() {
+            return String.format("property %s.%s", method.getDeclaringClass().getSimpleName(), property);
+        }
+
+        public String getName() {
+            return property;
+        }
+
+        public Class<?> getType() {
+            return method.getParameterTypes()[0];
+        }
+
+        public void setValue(Object target, Object value) {
+            try {
+                method.invoke(target, value);
+            } catch (InvocationTargetException e) {
+                throw UncheckedException.unwrapAndRethrow(e);
+            } catch (Exception e) {
+                throw UncheckedException.throwAsUncheckedException(e);
+            }
+        }
+    }
 }
diff --git a/subprojects/base-services/src/main/java/org/gradle/internal/reflect/NoSuchPropertyException.java b/subprojects/base-services/src/main/java/org/gradle/internal/reflect/NoSuchPropertyException.java
new file mode 100644
index 0000000..9f96809
--- /dev/null
+++ b/subprojects/base-services/src/main/java/org/gradle/internal/reflect/NoSuchPropertyException.java
@@ -0,0 +1,26 @@
+/*
+ * Copyright 2013 the original author or authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.gradle.internal.reflect;
+
+/**
+ * Thrown when a requested property cannot be found.
+ */
+public class NoSuchPropertyException extends RuntimeException {
+    public NoSuchPropertyException(String message) {
+        super(message);
+    }
+}
diff --git a/subprojects/base-services/src/main/java/org/gradle/internal/reflect/PropertyAccessor.java b/subprojects/base-services/src/main/java/org/gradle/internal/reflect/PropertyAccessor.java
new file mode 100644
index 0000000..3d02e6c
--- /dev/null
+++ b/subprojects/base-services/src/main/java/org/gradle/internal/reflect/PropertyAccessor.java
@@ -0,0 +1,25 @@
+/*
+ * Copyright 2013 the original author or authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.gradle.internal.reflect;
+
+public interface PropertyAccessor {
+    String getName();
+
+    Class<?> getType();
+
+    Object getValue(Object target);
+}
diff --git a/subprojects/base-services/src/main/java/org/gradle/internal/reflect/PropertyMutator.java b/subprojects/base-services/src/main/java/org/gradle/internal/reflect/PropertyMutator.java
new file mode 100644
index 0000000..9780742
--- /dev/null
+++ b/subprojects/base-services/src/main/java/org/gradle/internal/reflect/PropertyMutator.java
@@ -0,0 +1,25 @@
+/*
+ * Copyright 2013 the original author or authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.gradle.internal.reflect;
+
+public interface PropertyMutator {
+    String getName();
+
+    Class<?> getType();
+
+    void setValue(Object target, Object value);
+}
diff --git a/subprojects/base-services/src/main/java/org/gradle/internal/service/ServiceLocator.java b/subprojects/base-services/src/main/java/org/gradle/internal/service/ServiceLocator.java
index dacebfa..6487fd7 100644
--- a/subprojects/base-services/src/main/java/org/gradle/internal/service/ServiceLocator.java
+++ b/subprojects/base-services/src/main/java/org/gradle/internal/service/ServiceLocator.java
@@ -25,15 +25,14 @@ import java.io.IOException;
 import java.io.InputStream;
 import java.io.InputStreamReader;
 import java.net.URL;
-import java.util.Map;
-import java.util.concurrent.ConcurrentHashMap;
+import java.util.*;
 
 /**
  * Uses the Jar service resource specification to locate service implementations.
  */
 public class ServiceLocator extends AbstractServiceRegistry {
     private final ClassLoader classLoader;
-    private final Map<Class<?>, Object> implementations = new ConcurrentHashMap<Class<?>, Object>();
+    private final Map<Class<?>, Object> implementations = new HashMap<Class<?>, Object>();
 
     public ServiceLocator(ClassLoader classLoader) {
         this.classLoader = classLoader;
@@ -50,6 +49,15 @@ public class ServiceLocator extends AbstractServiceRegistry {
         }
     }
 
+    public <T> List<T> getAll(Class<T> serviceType) {
+        List<ServiceFactory<T>> factories = findFactoriesForServiceType(serviceType);
+        ArrayList<T> services = new ArrayList<T>();
+        for (ServiceFactory<T> factory : factories) {
+            services.add(factory.create());
+        }
+        return services;
+    }
+
     public <T> ServiceFactory<T> getFactory(final Class<T> serviceType) throws UnknownServiceException {
         ServiceFactory<T> factory = findFactory(serviceType);
         if (factory == null) {
@@ -62,55 +70,79 @@ public class ServiceLocator extends AbstractServiceRegistry {
      * Locates a factory for a given service. Returns null when no service implementation is available.
      */
     public <T> ServiceFactory<T> findFactory(Class<T> serviceType) {
-        Class<? extends T> implementationClass = findServiceImplementationClass(serviceType);
-        if (implementationClass == null) {
+        List<ServiceFactory<T>> factories = findFactoriesForServiceType(serviceType);
+        if (factories.isEmpty()) {
             return null;
         }
-        return new ServiceFactory<T>(serviceType, implementationClass);
+        return factories.get(0);
     }
 
-    <T> Class<? extends T> findServiceImplementationClass(Class<T> serviceType) {
-        String implementationClassName;
+    private <T> List<ServiceFactory<T>> findFactoriesForServiceType(Class<T> serviceType) {
+        List<Class<? extends T>> implementationClasses;
         try {
-            implementationClassName = findServiceImplementationClassName(serviceType);
+            implementationClasses = findServiceImplementations(serviceType);
+        } catch (ServiceLookupException e) {
+            throw e;
         } catch (Exception e) {
-            throw new ServiceLookupException(String.format("Could not determine implementation class for service '%s'.", serviceType.getName()), e);
-        }
-        if (implementationClassName == null) {
-            return null;
+            throw new ServiceLookupException(String.format("Could not determine implementation classes for service '%s'.", serviceType.getName()), e);
         }
-        try {
-            Class<?> implClass = classLoader.loadClass(implementationClassName);
-            if (!serviceType.isAssignableFrom(implClass)) {
-                throw new RuntimeException(String.format("Implementation class '%s' is not assignable to service class '%s'.", implementationClassName, serviceType.getName()));
-            }
-            return implClass.asSubclass(serviceType);
-        } catch (Throwable t) {
-            throw new ServiceLookupException(String.format("Could not load implementation class '%s' for service '%s'.", implementationClassName, serviceType.getName()), t);
+        List<ServiceFactory<T>> factories = new ArrayList<ServiceFactory<T>>();
+        for (Class<? extends T> implementationClass : implementationClasses) {
+            factories.add(new ServiceFactory<T>(serviceType, implementationClass));
         }
+        return factories;
     }
 
-    private String findServiceImplementationClassName(Class<?> serviceType) throws IOException {
+    private <T> List<Class<? extends T>> findServiceImplementations(Class<T> serviceType) throws IOException {
         String resourceName = "META-INF/services/" + serviceType.getName();
-        URL resource = classLoader.getResource(resourceName);
-        if (resource == null) {
-            return null;
+        Enumeration<URL> resources = classLoader.getResources(resourceName);
+        Set<String> implementationClassNames = new HashSet<String>();
+        List<Class<? extends T>> implementations = new ArrayList<Class<? extends T>>();
+        while (resources.hasMoreElements()) {
+            URL resource = resources.nextElement();
+            List<String> implementationClassNamesFromResource;
+            try {
+                implementationClassNamesFromResource = extractImplementationClassNames(resource);
+                if (implementationClassNamesFromResource.isEmpty()) {
+                    throw new RuntimeException(String.format("No implementation class for service '%s' specified.", serviceType.getName()));
+                }
+            } catch (Exception e) {
+                throw new ServiceLookupException(String.format("Could not determine implementation class for service '%s' specified in resource '%s'.", serviceType.getName(), resource), e);
+            }
+
+            for (String implementationClassName : implementationClassNamesFromResource) {
+                if (implementationClassNames.add(implementationClassName)) {
+                    try {
+                        Class<?> implClass = classLoader.loadClass(implementationClassName);
+                        if (!serviceType.isAssignableFrom(implClass)) {
+                            throw new RuntimeException(String.format("Implementation class '%s' is not assignable to service class '%s'.", implementationClassName, serviceType.getName()));
+                        }
+                        implementations.add(implClass.asSubclass(serviceType));
+                    } catch (Exception e) {
+                        throw new ServiceLookupException(String.format("Could not load implementation class '%s' for service '%s' specified in resource '%s'.", implementationClassName, serviceType.getName(), resource), e);
+                    }
+                }
+            }
         }
+        return implementations;
+    }
 
+    private List<String> extractImplementationClassNames(URL resource) throws IOException {
         InputStream inputStream = resource.openStream();
         try {
-            BufferedReader reader = new BufferedReader(new InputStreamReader(inputStream));
+            BufferedReader reader = new BufferedReader(new InputStreamReader(inputStream, "UTF-8"));
+            List<String> implemetationClassNames = new ArrayList<String>();
             String line;
             while ((line = reader.readLine()) != null) {
                 line = line.replaceAll("#.*", "").trim();
                 if (line.length() > 0) {
-                    return line;
+                    implemetationClassNames.add(line);
                 }
             }
+            return implemetationClassNames;
         } finally {
             inputStream.close();
         }
-        throw new RuntimeException(String.format("No implementation class for service '%s' specified in resource '%s'.", serviceType.getName(), resource));
     }
 
     public static class ServiceFactory<T> implements Factory<T> {
diff --git a/subprojects/base-services/src/main/java/org/gradle/internal/service/SynchronizedServiceRegistry.java b/subprojects/base-services/src/main/java/org/gradle/internal/service/SynchronizedServiceRegistry.java
index 868ba39..2a6c046 100644
--- a/subprojects/base-services/src/main/java/org/gradle/internal/service/SynchronizedServiceRegistry.java
+++ b/subprojects/base-services/src/main/java/org/gradle/internal/service/SynchronizedServiceRegistry.java
@@ -21,9 +21,6 @@ import org.gradle.internal.concurrent.Synchronizer;
 
 import java.lang.reflect.Type;
 
-/**
- * by Szczepan Faber, created at: 11/24/11
- */
 public class SynchronizedServiceRegistry implements ServiceRegistry {
     private final Synchronizer synchronizer = new Synchronizer();
     private final ServiceRegistry delegate;
diff --git a/subprojects/base-services/src/main/java/org/gradle/util/CollectionUtils.java b/subprojects/base-services/src/main/java/org/gradle/util/CollectionUtils.java
index 3b790f3..cf52c32 100644
--- a/subprojects/base-services/src/main/java/org/gradle/util/CollectionUtils.java
+++ b/subprojects/base-services/src/main/java/org/gradle/util/CollectionUtils.java
@@ -37,6 +37,10 @@ public abstract class CollectionUtils {
         return null;
     }
 
+    public static <T> boolean any(Iterable<? extends T> source, Spec<? super T> filter) {
+        return findFirst(source, filter) != null;
+    }
+
     public static <T> Set<T> filter(Set<? extends T> set, Spec<? super T> filter) {
         return filter(set, new LinkedHashSet<T>(), filter);
     }
@@ -104,6 +108,10 @@ public abstract class CollectionUtils {
         return collect(set, new HashSet<R>(), transformer);
     }
 
+    public static <R, I> List<R> collect(Iterable<? extends I> source, Transformer<? extends R, ? super I> transformer) {
+        return collect(source, new LinkedList<R>(), transformer);
+    }
+
     public static <R, I, C extends Collection<R>> C collect(Iterable<? extends I> source, C destination, Transformer<? extends R, ? super I> transformer) {
         for (I item : source) {
             destination.add(transformer.transform(item));
@@ -193,6 +201,18 @@ public abstract class CollectionUtils {
         return list;
     }
 
+    public static <T> List<T> toList(T[] things) {
+        if (things == null || things.length == 0) {
+            return new ArrayList<T>(0);
+        }
+
+        List<T> list = new ArrayList<T>(things.length);
+        for (T thing : things) {
+            list.add(thing);
+        }
+        return list;
+    }
+
     public static <T> Set<T> toSet(Iterable<? extends T> things) {
         if (things == null) {
             return new HashSet<T>(0);
diff --git a/subprojects/base-services/src/main/java/org/gradle/util/JavaMethod.java b/subprojects/base-services/src/main/java/org/gradle/util/JavaMethod.java
new file mode 100644
index 0000000..73420db
--- /dev/null
+++ b/subprojects/base-services/src/main/java/org/gradle/util/JavaMethod.java
@@ -0,0 +1,75 @@
+/*
+ * Copyright 2012 the original author or authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.gradle.util;
+
+import org.gradle.api.GradleException;
+
+import java.lang.reflect.InvocationTargetException;
+import java.lang.reflect.Method;
+import java.lang.reflect.Modifier;
+import java.util.Arrays;
+
+public class JavaMethod<T, R> {
+    private final Method method;
+    private final Class<R> returnType;
+
+    public JavaMethod(Class<T> target, Class<R> returnType, String name, Class<?>... paramTypes) {
+        this.returnType = returnType;
+        method = findMethod(target, target, name, paramTypes);
+        method.setAccessible(true);
+    }
+
+    public JavaMethod(Class<T> target, Class<R> returnType, Method method) {
+        this.returnType = returnType;
+        this.method = method;
+        method.setAccessible(true);
+    }
+
+    private Method findMethod(Class origTarget, Class target, String name, Class<?>[] paramTypes) {
+        for (Method method : target.getDeclaredMethods()) {
+            if (Modifier.isStatic(method.getModifiers())) {
+                continue;
+            }
+            if (method.getName().equals(name) && Arrays.equals(method.getParameterTypes(), paramTypes)) {
+                return method;
+            }
+        }
+
+        Class<?> parent = target.getSuperclass();
+        if (parent == null) {
+            throw new GradleException(String.format("Could not find method %s(%s) on %s", name, Arrays.toString(paramTypes),
+                    origTarget));
+        } else {
+            return findMethod(origTarget, parent, name, paramTypes);
+        }
+    }
+
+    public R invoke(T target, Object... args) {
+        try {
+            Object result = method.invoke(target, args);
+            return returnType.cast(result);
+        } catch (InvocationTargetException e) {
+            Throwable cause = e.getCause();
+            if (cause instanceof RuntimeException) {
+                throw (RuntimeException) cause;
+            }
+            throw new GradleException(String.format("Could not call %s.%s() on %s", method.getDeclaringClass().getSimpleName(), method.getName(), target), cause);
+        } catch (Exception e) {
+            throw new GradleException(String.format("Could not call %s.%s() on %s", method.getDeclaringClass().getSimpleName(), method.getName(), target), e);
+        }
+    }
+
+}
diff --git a/subprojects/core/src/main/groovy/org/gradle/util/hash/HashUtil.java b/subprojects/base-services/src/main/java/org/gradle/util/hash/HashUtil.java
similarity index 100%
rename from subprojects/core/src/main/groovy/org/gradle/util/hash/HashUtil.java
rename to subprojects/base-services/src/main/java/org/gradle/util/hash/HashUtil.java
diff --git a/subprojects/core/src/main/groovy/org/gradle/util/hash/HashValue.java b/subprojects/base-services/src/main/java/org/gradle/util/hash/HashValue.java
similarity index 100%
rename from subprojects/core/src/main/groovy/org/gradle/util/hash/HashValue.java
rename to subprojects/base-services/src/main/java/org/gradle/util/hash/HashValue.java
diff --git a/subprojects/base-services/src/test/groovy/org/gradle/api/internal/TransformersTest.groovy b/subprojects/base-services/src/test/groovy/org/gradle/api/internal/TransformersTest.groovy
index 6a95706..93e4102 100644
--- a/subprojects/base-services/src/test/groovy/org/gradle/api/internal/TransformersTest.groovy
+++ b/subprojects/base-services/src/test/groovy/org/gradle/api/internal/TransformersTest.groovy
@@ -16,6 +16,7 @@
 
 package org.gradle.api.internal
 
+import org.gradle.api.Action
 import org.gradle.api.Named
 import org.gradle.api.Namer
 import spock.lang.Specification
@@ -49,6 +50,11 @@ class TransformersTest extends Specification {
         asString().transform(null) == null
     }
 
+    def "to URL"() {
+        expect:
+        toURL().transform(new URI("http://localhost:80/path")) == new URL("http://localhost:80/path")
+    }
+
     def "naming"() {
         expect:
         name().transform(named("foo")) == "foo"
@@ -64,6 +70,24 @@ class TransformersTest extends Specification {
         name(namer).transform(3) == "3"
     }
 
+    def "by type"() {
+        expect:
+        type().transform("foo") == String
+    }
+
+    def "action as transformer"() {
+        def action = Mock(Action)
+
+        when:
+        def result = toTransformer(action).transform("original")
+
+        then:
+        1 * action.execute("original")
+
+        and:
+        result == null
+    }
+
     Named named(String name) {
         new Named() {
             String getName() {
diff --git a/subprojects/base-services/src/test/groovy/org/gradle/internal/LazyIterableTest.groovy b/subprojects/base-services/src/test/groovy/org/gradle/internal/LazyIterableTest.groovy
deleted file mode 100644
index 717b5ad..0000000
--- a/subprojects/base-services/src/test/groovy/org/gradle/internal/LazyIterableTest.groovy
+++ /dev/null
@@ -1,37 +0,0 @@
-/*
- * Copyright 2012 the original author or authors.
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- *      http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package org.gradle.internal
-
-import spock.lang.Specification
-
-class LazyIterableTest extends Specification {
-
-    def "laziness"() {
-        given:
-        def l = [1]
-        def i = new LazyIterable({ l } as Factory)
-
-        expect:
-        i.collect() == [1]
-
-        when:
-        l << 2
-
-        then:
-        i.collect() == [1, 2]
-    }
-}
diff --git a/subprojects/base-services/src/test/groovy/org/gradle/internal/classloader/CachingClassLoaderTest.groovy b/subprojects/base-services/src/test/groovy/org/gradle/internal/classloader/CachingClassLoaderTest.groovy
new file mode 100644
index 0000000..53991c6
--- /dev/null
+++ b/subprojects/base-services/src/test/groovy/org/gradle/internal/classloader/CachingClassLoaderTest.groovy
@@ -0,0 +1,78 @@
+/*
+ * Copyright 2013 the original author or authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.gradle.internal.classloader
+
+import spock.lang.Specification
+
+class CachingClassLoaderTest extends Specification {
+    final parent = Mock(ClassLoader, useObjenesis: false)
+    final classLoader = new CachingClassLoader(parent)
+
+    def "loads class once and caches result"() {
+        when:
+        def cl = classLoader.loadClass("someClass")
+
+        then:
+        cl == String.class
+
+        and:
+        1 * parent.loadClass("someClass", false) >> String.class
+        0 * parent._
+
+        when:
+        cl = classLoader.loadClass("someClass")
+
+        then:
+        cl == String.class
+
+        and:
+        0 * parent._
+    }
+
+    def "caches missing classes"() {
+        when:
+        classLoader.loadClass("someClass")
+
+        then:
+        thrown(ClassNotFoundException)
+
+        and:
+        1 * parent.loadClass("someClass", false) >> { throw new ClassNotFoundException("broken") }
+        0 * parent._
+
+        when:
+        classLoader.loadClass("someClass")
+
+        then:
+        thrown(ClassNotFoundException)
+
+        and:
+        0 * parent._
+    }
+
+    def "visits self and parent"() {
+        def visitor = Mock(ClassLoaderVisitor)
+
+        when:
+        classLoader.visit(visitor)
+
+        then:
+        1 * visitor.visitSpec({it instanceof CachingClassLoader.Spec})
+        1 * visitor.visitParent(parent)
+        0 * visitor._
+    }
+}
diff --git a/subprojects/base-services/src/test/groovy/org/gradle/internal/classloader/DefaultClassLoaderFactoryTest.groovy b/subprojects/base-services/src/test/groovy/org/gradle/internal/classloader/DefaultClassLoaderFactoryTest.groovy
new file mode 100644
index 0000000..13248fa
--- /dev/null
+++ b/subprojects/base-services/src/test/groovy/org/gradle/internal/classloader/DefaultClassLoaderFactoryTest.groovy
@@ -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.internal.classloader
+
+import spock.lang.Specification
+
+class DefaultClassLoaderFactoryTest extends Specification {
+    final DefaultClassLoaderFactory factory = new DefaultClassLoaderFactory()
+    ClassLoader original
+
+    def setup() {
+        original = Thread.currentThread().contextClassLoader
+    }
+
+    def cleanup() {
+        Thread.currentThread().contextClassLoader = original
+    }
+
+    def "classes from specified URLs are visible in isolated ClassLoader"() {
+        when:
+        def cl = factory.createIsolatedClassLoader(classpath)
+        def c = cl.loadClass(DefaultClassLoaderFactoryTestHelper.name)
+
+        then:
+        c.name == DefaultClassLoaderFactoryTestHelper.name
+        c != DefaultClassLoaderFactoryTestHelper
+    }
+
+    def "application classes are not visible in isolated ClassLoader"() {
+        when:
+        def cl = factory.createIsolatedClassLoader(classpath)
+        cl.loadClass(Closure.name)
+
+        then:
+        thrown(ClassNotFoundException)
+    }
+
+    def "can use XML APIs from isolated ClassLoader when application classes include an XML provider"() {
+        assert ClassLoader.getSystemResource("META-INF/services/javax.xml.parsers.SAXParserFactory")
+
+        when:
+        def cl = factory.createIsolatedClassLoader(classpath)
+        def c = cl.loadClass(DefaultClassLoaderFactoryTestHelper.name)
+
+        then:
+        c != DefaultClassLoaderFactoryTestHelper
+
+        when:
+        Thread.currentThread().contextClassLoader = cl
+        c.newInstance().doStuff()
+
+        then:
+        notThrown()
+    }
+
+    def "can use XML APIs from filtering ClassLoader when application classes include an XML provider"() {
+        assert ClassLoader.getSystemResource("META-INF/services/javax.xml.parsers.SAXParserFactory")
+
+        when:
+        def cl = new URLClassLoader(classpath.collect { it.toURL() } as URL[], factory.createFilteringClassLoader(getClass().classLoader))
+        def c = cl.loadClass(DefaultClassLoaderFactoryTestHelper.name)
+
+        then:
+        c != DefaultClassLoaderFactoryTestHelper
+
+        when:
+        Thread.currentThread().contextClassLoader = cl
+        c.newInstance().doStuff()
+
+        then:
+        notThrown()
+    }
+
+    def getClasspath() {
+        return [ClasspathUtil.getClasspathForClass(DefaultClassLoaderFactoryTestHelper)].collect { it.toURI() }
+    }
+}
+
diff --git a/subprojects/base-services/src/test/groovy/org/gradle/internal/classloader/DefaultClassLoaderFactoryTestHelper.java b/subprojects/base-services/src/test/groovy/org/gradle/internal/classloader/DefaultClassLoaderFactoryTestHelper.java
new file mode 100644
index 0000000..d1d4ee0
--- /dev/null
+++ b/subprojects/base-services/src/test/groovy/org/gradle/internal/classloader/DefaultClassLoaderFactoryTestHelper.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.internal.classloader;
+
+import javax.xml.XMLConstants;
+import javax.xml.datatype.DatatypeFactory;
+import javax.xml.parsers.DocumentBuilderFactory;
+import javax.xml.parsers.SAXParserFactory;
+import javax.xml.transform.TransformerFactory;
+import javax.xml.validation.SchemaFactory;
+import javax.xml.xpath.XPathFactory;
+
+public class DefaultClassLoaderFactoryTestHelper {
+    public void doStuff() throws Exception {
+        SAXParserFactory.newInstance().newSAXParser();
+        DocumentBuilderFactory.newInstance().newDocumentBuilder();
+        DatatypeFactory.newInstance().newXMLGregorianCalendar();
+        TransformerFactory.newInstance().newTransformer();
+        SchemaFactory.newInstance(XMLConstants.W3C_XML_SCHEMA_NS_URI);
+        XPathFactory.newInstance().newXPath();
+    }
+}
diff --git a/subprojects/base-services/src/test/groovy/org/gradle/internal/classloader/FilteringClassLoaderTest.groovy b/subprojects/base-services/src/test/groovy/org/gradle/internal/classloader/FilteringClassLoaderTest.groovy
new file mode 100644
index 0000000..9a7d5e8
--- /dev/null
+++ b/subprojects/base-services/src/test/groovy/org/gradle/internal/classloader/FilteringClassLoaderTest.groovy
@@ -0,0 +1,235 @@
+/*
+ * Copyright 2010 the original author or authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.gradle.internal.classloader
+
+import org.junit.Before
+import org.junit.Test
+import org.junit.runners.BlockJUnit4ClassRunner
+import spock.lang.Specification
+
+import static org.junit.Assert.fail
+
+class FilteringClassLoaderTest extends Specification {
+    private final FilteringClassLoader classLoader = new FilteringClassLoader(FilteringClassLoaderTest.class.getClassLoader())
+
+    void passesThroughSystemClasses() {
+        expect:
+        canLoadClass(String)
+    }
+
+    void passesThroughSystemPackages() {
+        expect:
+        canSeePackage('java.lang')
+    }
+
+    void passesThroughSystemResources() {
+        expect:
+        canSeeResource('com/sun/jndi/ldap/jndiprovider.properties')
+    }
+
+    void filtersClassesByDefault() {
+        given:
+        classLoader.parent.loadClass(Test.class.name)
+
+        when:
+        classLoader.loadClass(Test.class.name, false)
+
+        then:
+        ClassNotFoundException e = thrown()
+        e.message == "$Test.name not found."
+
+        when:
+        classLoader.loadClass(Test.class.name)
+
+        then:
+        ClassNotFoundException e2 = thrown()
+        e2.message == "$Test.name not found."
+    }
+
+    void filtersPackagesByDefault() {
+        given:
+        assert classLoader.parent.getPackage('org.junit') != null
+
+        expect:
+        cannotSeePackage('org.junit')
+    }
+
+    void filtersResourcesByDefault() {
+        given:
+        assert classLoader.parent.getResource('org/gradle/util/ClassLoaderTest.txt') != null
+
+        expect:
+        cannotSeeResource('org/gradle/util/ClassLoaderTest.txt')
+    }
+
+    void passesThroughClassesInSpecifiedPackagesAndSubPackages() {
+        given:
+        cannotLoadClass(Test)
+        cannotLoadClass(BlockJUnit4ClassRunner)
+
+        and:
+        classLoader.allowPackage('org.junit')
+
+        expect:
+        canLoadClass(Test)
+        canLoadClass(Before)
+        canLoadClass(BlockJUnit4ClassRunner)
+    }
+
+    void passesThroughSpecifiedClasses() {
+        given:
+        cannotLoadClass(Test)
+
+        and:
+        classLoader.allowClass(Test.class)
+
+        expect:
+        canLoadClass(Test)
+        cannotLoadClass(Before)
+    }
+
+    void filtersSpecifiedClasses() {
+        given:
+        cannotLoadClass(Test)
+        cannotLoadClass(Before)
+
+        and:
+        classLoader.allowPackage("org.junit")
+        classLoader.disallowClass("org.junit.Test")
+
+        expect:
+        canLoadClass(Before)
+        cannotLoadClass(Test)
+    }
+
+    void disallowClassWinsOverAllowClass() {
+        given:
+        classLoader.allowClass(Test)
+        classLoader.disallowClass(Test.name)
+
+        expect:
+        cannotLoadClass(Test)
+    }
+
+    void passesThroughSpecifiedPackagesAndSubPackages() {
+        given:
+        cannotSeePackage('org.junit')
+        cannotSeePackage('org.junit.runner')
+
+        and:
+        classLoader.allowPackage('org.junit')
+
+        expect:
+        canSeePackage('org.junit')
+        canSeePackage('org.junit.runner')
+    }
+
+    void passesThroughResourcesInSpecifiedPackages() {
+        given:
+        cannotSeeResource('org/gradle/util/ClassLoaderTest.txt')
+
+        classLoader.allowPackage('org.gradle')
+
+        expect:
+        canSeeResource('org/gradle/util/ClassLoaderTest.txt')
+    }
+
+    void passesThroughResourcesWithSpecifiedPrefix() {
+        given:
+        cannotSeeResource('org/gradle/util/ClassLoaderTest.txt')
+
+        and:
+        classLoader.allowResources('org/gradle')
+
+        expect:
+        canSeeResource('org/gradle/util/ClassLoaderTest.txt')
+    }
+
+    void passesThroughSpecifiedResources() {
+        given:
+        cannotSeeResource('org/gradle/util/ClassLoaderTest.txt')
+
+        and:
+        classLoader.allowResource('org/gradle/util/ClassLoaderTest.txt')
+
+        expect:
+        canSeeResource('org/gradle/util/ClassLoaderTest.txt')
+    }
+
+    void "visits self and parent"() {
+        def visitor = Mock(ClassLoaderVisitor)
+        given:
+        classLoader.allowClass(Test)
+        classLoader.allowPackage("org.junit")
+        classLoader.allowResource("a/b/c")
+        classLoader.disallowClass(Before.name)
+
+        when:
+        classLoader.visit(visitor)
+
+        then:
+        1 * visitor.visitSpec({it instanceof FilteringClassLoader.Spec}) >> { FilteringClassLoader.Spec spec ->
+            spec.classNames == [Test.name]
+            spec.disallowedClassNames == [Before.name]
+            spec.packageNames == ["org.junit"]
+            spec.packagePrefixes == ["org.junit."]
+            spec.resourceNames == ["a/b/c"]
+            spec.resourcePrefixes == ["org/junit/"]
+        }
+        1 * visitor.visitParent(classLoader.parent)
+        0 * visitor._
+    }
+
+    void cannotSeeResource(String name) {
+        assert classLoader.getResource(name) == null
+        assert classLoader.getResourceAsStream(name) == null
+        assert !classLoader.getResources(name).hasMoreElements()
+    }
+
+    void canSeeResource(String name) {
+        assert classLoader.getResource(name) != null
+        def instr = classLoader.getResourceAsStream(name)
+        assert instr != null
+        instr.close()
+        assert classLoader.getResources(name).hasMoreElements()
+    }
+
+    void canSeePackage(String name) {
+        assert classLoader.getPackage(name) != null
+        assert classLoader.packages.any { it.name == name }
+    }
+
+    void cannotSeePackage(String name) {
+        assert classLoader.getPackage(name) == null
+        assert !classLoader.packages.any { it.name == name }
+    }
+
+    void canLoadClass(Class<?> clazz) {
+        assert classLoader.loadClass(clazz.name, false).is(clazz)
+        assert classLoader.loadClass(clazz.name).is(clazz)
+    }
+
+    void cannotLoadClass(Class<?> clazz) {
+        try {
+            classLoader.loadClass(clazz.name, false)
+            fail()
+        } catch (ClassNotFoundException expected) {}
+        try {
+            classLoader.loadClass(clazz.name)
+            fail()
+        } catch (ClassNotFoundException expected) {}
+    }
+}
diff --git a/subprojects/base-services/src/test/groovy/org/gradle/internal/classloader/MultiParentClassLoaderTest.groovy b/subprojects/base-services/src/test/groovy/org/gradle/internal/classloader/MultiParentClassLoaderTest.groovy
new file mode 100644
index 0000000..0faa891
--- /dev/null
+++ b/subprojects/base-services/src/test/groovy/org/gradle/internal/classloader/MultiParentClassLoaderTest.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.internal.classloader
+
+import spock.lang.Specification
+
+class MultiParentClassLoaderTest extends Specification {
+    private ClassLoader parent1 = Mock()
+    private ClassLoader parent2 = Mock()
+    private MultiParentClassLoader loader = new MultiParentClassLoader(parent1, parent2)
+
+    public void parentsAreNotVisibleViaSuperClass() {
+        expect:
+        loader.parent == null
+    }
+
+    public void loadsClassFromParentsInOrderSpecified() {
+        given:
+        _ * parent1.loadClass('string') >> String
+        _ * parent1.loadClass('integer') >> { throw new ClassNotFoundException() }
+        _ * parent2.loadClass('integer') >> Integer
+
+        expect:
+        loader.loadClass('string') == String
+        loader.loadClass('string', true) == String
+        loader.loadClass('integer') == Integer
+        loader.loadClass('integer', true) == Integer
+    }
+
+    public void throwsCNFExceptionWhenClassNotFound() {
+        given:
+        _ * parent1.loadClass('string') >> { throw new ClassNotFoundException() }
+        _ * parent2.loadClass('string') >> { throw new ClassNotFoundException() }
+
+        when:
+        loader.loadClass('string')
+
+        then:
+        ClassNotFoundException e = thrown()
+        e.message == 'string not found.'
+    }
+    
+    public void loadsPackageFromParentsInOrderSpecified() {
+        def stringPackage = String.class.getPackage()
+        def listPackage = List.class.getPackage()
+
+        given:
+        _ * parent1.getPackage('string') >> stringPackage
+        _ * parent1.getPackage('list') >> null
+        _ * parent2.getPackage('list') >> listPackage
+
+        expect:
+        loader.getPackage('string') == stringPackage
+        loader.getPackage('list') == listPackage
+    }
+
+    public void containsUnionOfPackagesFromAllParents() {
+        def package1 = Stub(Package)
+        def package2 = Stub(Package)
+        def package3 = Stub(Package)
+
+        given:
+        _ * parent1.getPackages() >> ([package1] as Package[])
+        _ * parent2.getPackages() >> ([package1, package2, package3] as Package[])
+
+        expect:
+        loader.getPackages() == [package1, package2, package3] as Package[]
+    }
+
+    public void loadsResourceFromParentsInOrderSpecified() {
+        URL resource1 = new File('res1').toURI().toURL()
+        URL resource2 = new File('res2').toURI().toURL()
+
+        given:
+        _ * parent1.getResource('resource1') >> resource1
+        _ * parent1.getResource('resource2') >> null
+        _ * parent2.getResource('resource2') >> resource2
+
+        expect:
+        loader.getResource('resource1') == resource1
+        loader.getResource('resource2') == resource2
+    }
+    
+    public void containsUnionOfResourcesFromAllParents() {
+        URL resource1 = new File('res1').toURI().toURL()
+        URL resource2 = new File('res2').toURI().toURL()
+        URL resource3 = new File('res3').toURI().toURL()
+
+        given:
+        _ * parent1.getResources('resource') >> { return Collections.enumeration([resource1, resource2]) }
+        _ * parent2.getResources('resource') >> { return Collections.enumeration([resource1, resource3]) }
+
+        expect:
+        def resources = loader.getResources('resource').collect { it }
+        resources == [resource1, resource2, resource3]
+    }
+
+    public void visitsSelfAndParents() {
+        def visitor = Mock(ClassLoaderVisitor)
+
+        when:
+        loader.visit(visitor)
+
+        then:
+        1 * visitor.visitSpec({it instanceof MultiParentClassLoader.Spec})
+        1 * visitor.visitParent(parent1)
+        1 * visitor.visitParent(parent2)
+        0 * visitor._
+    }
+}
diff --git a/subprojects/base-services/src/test/groovy/org/gradle/internal/classloader/MutableURLClassLoaderTest.groovy b/subprojects/base-services/src/test/groovy/org/gradle/internal/classloader/MutableURLClassLoaderTest.groovy
new file mode 100644
index 0000000..5c04a80
--- /dev/null
+++ b/subprojects/base-services/src/test/groovy/org/gradle/internal/classloader/MutableURLClassLoaderTest.groovy
@@ -0,0 +1,39 @@
+/*
+ * Copyright 2013 the original author or authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.gradle.internal.classloader
+
+import spock.lang.Specification
+
+class MutableURLClassLoaderTest extends Specification {
+    def "visits self and parent"() {
+        def visitor = Mock(ClassLoaderVisitor)
+        def parent = new ClassLoader(null) { }
+        def classPath = [new File("a").toURI().toURL(), new File("b").toURI().toURL()]
+        def cl = new MutableURLClassLoader(parent, classPath)
+
+        when:
+        cl.visit(visitor)
+
+        then:
+        1 * visitor.visitSpec({it instanceof MutableURLClassLoader.Spec}) >> { MutableURLClassLoader.Spec spec ->
+            assert spec.classpath == classPath
+        }
+        1 * visitor.visitClassPath(classPath)
+        1 * visitor.visitParent(parent)
+        0 * visitor._
+    }
+}
diff --git a/subprojects/base-services/src/test/groovy/org/gradle/internal/concurrent/ServiceLifecycleTest.groovy b/subprojects/base-services/src/test/groovy/org/gradle/internal/concurrent/ServiceLifecycleTest.groovy
new file mode 100644
index 0000000..c25ec4d
--- /dev/null
+++ b/subprojects/base-services/src/test/groovy/org/gradle/internal/concurrent/ServiceLifecycleTest.groovy
@@ -0,0 +1,203 @@
+/*
+ * Copyright 2013 the original author or authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.gradle.internal.concurrent
+
+import org.gradle.test.fixtures.concurrent.ConcurrentSpec
+
+class ServiceLifecycleTest extends ConcurrentSpec {
+    def lifecycle = new ServiceLifecycle("[service]")
+
+    def "can use service when not stopped"() {
+        def action = Mock(Runnable)
+
+        when:
+        lifecycle.use(action)
+
+        then:
+        1 * action.run()
+        0 * _._
+    }
+
+    def "can use service concurrently from multiple threads"() {
+        given:
+        def action1 = {
+            instant.action1Started
+            thread.blockUntil.action2Done
+            instant.action1Done
+        }
+        def action2 = {
+            thread.blockUntil.action1Started
+            instant.action2Done
+        }
+
+        when:
+        async {
+            start {
+                lifecycle.use(action1)
+            }
+            start {
+                lifecycle.use(action2)
+            }
+        }
+
+        then:
+        instant.action1Done > instant.action2Done
+    }
+
+    def "can stop multiple times"() {
+        when:
+        lifecycle.stop()
+        lifecycle.stop()
+        lifecycle.requestStop()
+        lifecycle.requestStop()
+        lifecycle.stop()
+
+        then:
+        noExceptionThrown()
+    }
+
+    def "throws exception when attempting to use service after it has stopped"() {
+        when:
+        lifecycle.stop()
+        lifecycle.use { }
+
+        then:
+        IllegalStateException e = thrown()
+        e.message == 'Cannot use [service] as it has been stopped.'
+    }
+
+    def "throws exception when attempting to use service after stop has been requested"() {
+        when:
+        lifecycle.requestStop()
+        lifecycle.use { }
+
+        then:
+        IllegalStateException e = thrown()
+        e.message == 'Cannot use [service] as it has been stopped.'
+    }
+
+    def "throws exception when attempting to use service while it is stopping"() {
+        when:
+        async {
+            start {
+                lifecycle.use {
+                    lifecycle.requestStop()
+                    instant.stopRequested
+                    thread.block()
+                }
+            }
+            thread.blockUntil.stopRequested
+            lifecycle.use {}
+        }
+
+        then:
+        IllegalStateException e = thrown()
+        e.message == 'Cannot use [service] as it is currently stopping.'
+    }
+
+    def "stop() blocks while service is in use"() {
+        when:
+        async {
+            start {
+                lifecycle.use {
+                    instant.running
+                    thread.block()
+                    instant.finished
+                }
+            }
+            thread.blockUntil.running
+            lifecycle.stop()
+            instant.stopped
+        }
+
+        then:
+        instant.finished < instant.stopped
+    }
+
+    def "requestStop() does not block while service is in use"() {
+        when:
+        async {
+            start {
+                lifecycle.use {
+                    instant.running
+                    thread.blockUntil.requested
+                    instant.finished
+                }
+            }
+            thread.blockUntil.running
+            lifecycle.requestStop()
+            instant.requested
+        }
+
+        then:
+        instant.requested < instant.finished
+    }
+
+    def "cannot call stop() from thread that is using service"() {
+        when:
+        lifecycle.use {
+            lifecycle.stop()
+        }
+
+        then:
+        IllegalStateException e = thrown()
+        e.message == 'Cannot stop [service] from a thread that is using it.'
+    }
+
+    def "usage is re-entrant"() {
+        when:
+        async {
+            start {
+                lifecycle.use {
+                    instant.running
+                    lifecycle.use {
+                        lifecycle.use { }
+                        thread.block()
+                    }
+                    instant.finished
+                }
+            }
+            thread.blockUntil.running
+            lifecycle.stop()
+            instant.stopped
+        }
+
+        then:
+        instant.finished < instant.stopped
+    }
+
+    def "can call requestStop() from thread that is using service"() {
+        when:
+        async {
+            start {
+                lifecycle.use {
+                    lifecycle.requestStop()
+                    instant.stopRequested
+                    thread.block()
+                    instant.finished
+                }
+            }
+
+            thread.blockUntil.stopRequested
+            lifecycle.stop()
+            instant.stopped
+        }
+
+        then:
+        instant.finished < instant.stopped
+    }
+}
diff --git a/subprojects/base-services/src/test/groovy/org/gradle/internal/jvm/JvmTest.groovy b/subprojects/base-services/src/test/groovy/org/gradle/internal/jvm/JvmTest.groovy
index 91f81b2..8f9ca5b 100644
--- a/subprojects/base-services/src/test/groovy/org/gradle/internal/jvm/JvmTest.groovy
+++ b/subprojects/base-services/src/test/groovy/org/gradle/internal/jvm/JvmTest.groovy
@@ -26,7 +26,11 @@ import spock.lang.Specification
 class JvmTest extends Specification {
     @Rule TestNameTestDirectoryProvider tmpDir = new TestNameTestDirectoryProvider()
     @Rule SetSystemProperties sysProp = new SetSystemProperties()
-    OperatingSystem os = Mock()
+    OperatingSystem os = Mock() {
+        getExecutableName(_) >> { String name ->
+            return "${name}.exe"
+        }
+    }
     OperatingSystem theOs = OperatingSystem.current()
 
     Jvm getJvm() {
@@ -48,104 +52,213 @@ class JvmTest extends Specification {
         7       | 5      | 6
     }
 
-    def "looks for runtime Jar in Java home directory"() {
-        TestFile javaHomeDir = tmpDir.createDir('jdk')
-        TestFile runtimeJar = javaHomeDir.file('lib/rt.jar').createFile()
-        System.properties['java.home'] = javaHomeDir.absolutePath
-
-        expect:
-        jvm.javaHome == javaHomeDir
-        jvm.runtimeJar == runtimeJar
-    }
+    def "locates JDK and JRE installs when java.home points to a typical JRE installation embedded in a JDK installation"() {
+        given:
+        TestFile software = tmpDir.createDir('software')
+        software.create {
+            jdk {
+                lib {
+                    file 'tools.jar'
+                }
+                bin {
+                    file 'java.exe'
+                    file 'javac.exe'
+                    file 'javadoc.exe'
+                }
+                jre {
+                    lib { file 'rt.jar' }
+                    bin { file 'java.exe' }
+                }
+            }
+        }
 
-    def "looks for tools Jar in Java home directory"() {
-        TestFile javaHomeDir = tmpDir.createDir('jdk')
-        TestFile toolsJar = javaHomeDir.file('lib/tools.jar').createFile()
-        System.properties['java.home'] = javaHomeDir.absolutePath
+        when:
+        System.properties['java.home'] = software.file('jdk/jre').absolutePath
 
-        expect:
-        jvm.javaHome == javaHomeDir
-        jvm.toolsJar == toolsJar
+        then:
+        jvm.javaHome == software.file('jdk')
+        jvm.runtimeJar == software.file('jdk/jre/lib/rt.jar')
+        jvm.toolsJar == software.file('jdk/lib/tools.jar')
+        jvm.javaExecutable == software.file('jdk/bin/java.exe')
+        jvm.javacExecutable == software.file('jdk/bin/javac.exe')
+        jvm.javadocExecutable == software.file('jdk/bin/javadoc.exe')
+        jvm.jre.homeDir == software.file('jdk/jre')
+        jvm.standaloneJre == null
     }
 
-    def "provides information when typical jdk installed"() {
+    def "locates JDK and JRE installs when java.home points to a typical JDK installation"() {
         given:
         TestFile software = tmpDir.createDir('software')
         software.create {
             jdk {
-                jre { lib { file 'rt.jar' }}
-                lib { file 'tools.jar'}
+                lib {
+                    file 'tools.jar'
+                }
+                bin {
+                    file 'java.exe'
+                    file 'javac.exe'
+                    file 'javadoc.exe'
+                }
+                jre {
+                    lib { file 'rt.jar' }
+                    bin { file 'java.exe' }
+                }
             }
         }
 
         when:
-        System.properties['java.home'] = software.file('jdk/jre').absolutePath
+        System.properties['java.home'] = software.file('jdk').absolutePath
 
         then:
-        jvm.javaHome.absolutePath == software.file('jdk').absolutePath
+        jvm.javaHome == software.file('jdk')
         jvm.runtimeJar == software.file('jdk/jre/lib/rt.jar')
         jvm.toolsJar == software.file('jdk/lib/tools.jar')
+        jvm.javaExecutable == software.file('jdk/bin/java.exe')
+        jvm.javacExecutable == software.file('jdk/bin/javac.exe')
+        jvm.javadocExecutable == software.file('jdk/bin/javadoc.exe')
+        jvm.jre.homeDir == software.file('jdk/jre')
+        jvm.standaloneJre == null
     }
 
-    def "provides information when typical jre installed"() {
+    def "locates JDK and JRE installs when java.home points to a typical standalone JRE installation"() {
         given:
         TestFile software = tmpDir.createDir('software')
         software.create {
-            jre { lib { file 'rt.jar' }}
+            jre {
+                bin { file 'java.exe' }
+                lib { file 'rt.jar' }
+            }
         }
 
         when:
         System.properties['java.home'] = software.file('jre').absolutePath
 
         then:
-        jvm.javaHome.absolutePath == software.file('jre').absolutePath
+        jvm.javaHome == software.file('jre')
         jvm.runtimeJar == software.file('jre/lib/rt.jar')
         jvm.toolsJar == null
+        jvm.javaExecutable == software.file('jre/bin/java.exe')
+        jvm.javacExecutable == new File('javac.exe')
+        jvm.javadocExecutable == new File('javadoc.exe')
+        jvm.jre.homeDir == software.file('jre')
+        jvm.standaloneJre.homeDir == software.file('jre')
     }
 
-    def "looks for tools Jar in parent of JRE's Java home directory"() {
-        TestFile javaHomeDir = tmpDir.createDir('jdk')
-        TestFile toolsJar = javaHomeDir.file('lib/tools.jar').createFile()
-        System.properties['java.home'] = javaHomeDir.file('jre').absolutePath
+    def "locates JDK and JRE installs when java.home points to a typical standalone JRE installation on Windows"() {
+        given:
+        TestFile software = tmpDir.createDir('software')
+        software.create {
+            "${jreDirName}" {
+                bin { file 'java.exe' }
+                lib { file 'rt.jar' }
+            }
+            "${jdkDirName}" {
+                bin {
+                    file 'java.exe'
+                    file 'javac.exe'
+                    file 'javadoc.exe'
+                }
+                lib { file 'tools.jar' }
+            }
+        }
+        def jreDir = software.file(jreDirName)
+        def jdkDir = software.file(jdkDirName)
+
+        and:
+        _ * os.windows >> true
 
-        expect:
-        def jvm = new Jvm(os)
-        jvm.javaHome == javaHomeDir
-        jvm.toolsJar == toolsJar
+        when:
+        System.properties['java.home'] = jreDir.absolutePath
+        System.properties['java.version'] = version
+
+        then:
+        jvm.javaHome == jdkDir
+        jvm.runtimeJar == jreDir.file("lib/rt.jar")
+        jvm.toolsJar == jdkDir.file("lib/tools.jar")
+        jvm.javaExecutable == jdkDir.file('bin/java.exe')
+        jvm.javacExecutable == jdkDir.file('bin/javac.exe')
+        jvm.javadocExecutable == jdkDir.file('bin/javadoc.exe')
+        jvm.jre.homeDir == jreDir
+        jvm.standaloneJre.homeDir == jreDir
+
+        where:
+        version    | jreDirName    | jdkDirName
+        '1.6.0'    | 'jre6'        | 'jdk1.6.0'
+        '1.5.0_22' | 'jre1.5.0_22' | 'jdk1.5.0_22'
     }
 
-    def "looks for tools Jar in sibling of JRE's Java home directory on Windows"() {
-        TestFile javaHomeDir = tmpDir.createDir('jdk1.6.0')
-        TestFile toolsJar = javaHomeDir.file('lib/tools.jar').createFile()
-        System.properties['java.home'] = tmpDir.createDir('jre6').absolutePath
-        System.properties['java.version'] = '1.6.0'
+    def "locates JDK and JRE installs when java.home points to a typical JDK installation on Windows"() {
+        given:
+        TestFile software = tmpDir.createDir('software')
+        software.create {
+            "${jreDirName}" {
+                bin { file 'java.exe' }
+                lib {
+                    file 'rt.jar'
+                }
+            }
+            "${jdkDirName}" {
+                bin {
+                    file 'java.exe'
+                    file 'javac.exe'
+                    file 'javadoc.exe'
+                }
+                jre {
+                    lib {
+                        file 'rt.jar'
+                    }
+                }
+                lib {
+                    file 'tools.jar'
+                }
+            }
+        }
+        def jreDir = software.file(jreDirName)
+        def jdkDir = software.file(jdkDirName)
+
+        and:
         _ * os.windows >> true
 
-        expect:
-        jvm.javaHome == javaHomeDir
-        jvm.toolsJar == toolsJar
+        when:
+        System.properties['java.home'] = jdkDir.absolutePath
+        System.properties['java.version'] = version
+
+        then:
+        jvm.javaHome == jdkDir
+        jvm.runtimeJar == jdkDir.file("jre/lib/rt.jar")
+        jvm.toolsJar == jdkDir.file("lib/tools.jar")
+        jvm.javaExecutable == jdkDir.file('bin/java.exe')
+        jvm.javacExecutable == jdkDir.file('bin/javac.exe')
+        jvm.javadocExecutable == jdkDir.file('bin/javadoc.exe')
+        jvm.jre.homeDir == jdkDir.file('jre')
+        jvm.standaloneJre.homeDir == jreDir
+
+        where:
+        version    | jreDirName    | jdkDirName
+        '1.6.0'    | 'jre6'        | 'jdk1.6.0'
+        '1.5.0_22' | 'jre1.5.0_22' | 'jdk1.5.0_22'
     }
 
-    def "uses system property to locate Java home directory when tools Jar not found"() {
-        TestFile javaHomeDir = tmpDir.createDir('jdk')
-        System.properties['java.home'] = javaHomeDir.absolutePath
+    def "uses system property to determine if Sun/Oracle JVM"() {
+        when:
+        System.properties['java.vm.vendor'] = 'Sun'
+        def jvm = Jvm.create(null)
 
-        expect:
-        jvm.javaHome == javaHomeDir
-        jvm.toolsJar == null
+        then:
+        jvm.getClass() == Jvm
     }
 
     def "uses system property to determine if Apple JVM"() {
         when:
         System.properties['java.vm.vendor'] = 'Apple Inc.'
-        def jvm = Jvm.current()
+        def jvm = Jvm.create(null)
 
         then:
         jvm.getClass() == Jvm.AppleJvm
 
         when:
         System.properties['java.vm.vendor'] = 'Sun'
-        jvm = Jvm.current()
+        jvm = Jvm.create(null)
 
         then:
         jvm.getClass() == Jvm
@@ -154,13 +267,13 @@ class JvmTest extends Specification {
     def "uses system property to determine if IBM JVM"() {
         when:
         System.properties['java.vm.vendor'] = 'IBM Corporation'
-        def jvm = Jvm.current()
+        def jvm = Jvm.create(null)
 
         then:
         jvm.getClass() == Jvm.IbmJvm
     }
 
-    def "finds executable if for java home supplied"() {
+    def "finds executable for java home supplied"() {
         System.properties['java.vm.vendor'] = 'Sun'
 
         when:
@@ -176,7 +289,7 @@ class JvmTest extends Specification {
 
         then:
         home.file(theOs.getExecutableName("jre/bin/javadoc")).absolutePath ==
-            Jvm.forHome(home.file("jre")).getExecutable("javadoc").absolutePath
+                Jvm.forHome(home.file("jre")).getExecutable("javadoc").absolutePath
     }
 
     def "finds tools.jar if java home supplied"() {
@@ -193,7 +306,7 @@ class JvmTest extends Specification {
 
         then:
         home.file("jdk/lib/tools.jar").absolutePath ==
-            Jvm.forHome(home.file("jdk")).toolsJar.absolutePath
+                Jvm.forHome(home.file("jdk")).toolsJar.absolutePath
     }
 
     def "provides decent feedback if executable not found"() {
@@ -215,8 +328,7 @@ class JvmTest extends Specification {
         given:
         def home = tmpDir.createDir("home")
         System.properties['java.home'] = home.absolutePath
-        1 * os.getExecutableName(_ as String) >> "foobar.exe"
-        1 * os.findInPath("foobar") >> new File('/path/foobar.exe')
+        _ * os.findInPath("foobar") >> new File('/path/foobar.exe')
 
         when:
         def exec = jvm.getExecutable("foobar")
@@ -259,7 +371,7 @@ class JvmTest extends Specification {
         then:
         thrown(IllegalArgumentException)
     }
-    
+
     def "describes accurately when created for supplied java home"() {
         when:
         def jvm = new Jvm(theOs, new File('dummyFolder'))
diff --git a/subprojects/base-services/src/test/groovy/org/gradle/internal/os/OperatingSystemTest.groovy b/subprojects/base-services/src/test/groovy/org/gradle/internal/os/OperatingSystemTest.groovy
index 71e41da..c9adf6a 100644
--- a/subprojects/base-services/src/test/groovy/org/gradle/internal/os/OperatingSystemTest.groovy
+++ b/subprojects/base-services/src/test/groovy/org/gradle/internal/os/OperatingSystemTest.groovy
@@ -61,6 +61,9 @@ class OperatingSystemTest extends Specification {
         os.getScriptName("a.bat") == "a.bat"
         os.getScriptName("a.BAT") == "a.BAT"
         os.getScriptName("a") == "a.bat"
+        os.getScriptName("a.exe") == "a.bat"
+        os.getScriptName("a.b/c") == "a.b/c.bat"
+        os.getScriptName("a.b\\c") == "a.b\\c.bat"
     }
 
     def "windows transforms executable names"() {
@@ -70,6 +73,9 @@ class OperatingSystemTest extends Specification {
         os.getExecutableName("a.exe") == "a.exe"
         os.getExecutableName("a.EXE") == "a.EXE"
         os.getExecutableName("a") == "a.exe"
+        os.getExecutableName("a.bat") == "a.exe"
+        os.getExecutableName("a.b/c") == "a.b/c.exe"
+        os.getExecutableName("a.b\\c") == "a.b\\c.exe"
     }
 
     def "windows transforms shared library names"() {
@@ -79,6 +85,21 @@ class OperatingSystemTest extends Specification {
         os.getSharedLibraryName("a.dll") == "a.dll"
         os.getSharedLibraryName("a.DLL") == "a.DLL"
         os.getSharedLibraryName("a") == "a.dll"
+        os.getSharedLibraryName("a.lib") == "a.dll"
+        os.getSharedLibraryName("a.b/c") == "a.b/c.dll"
+        os.getSharedLibraryName("a.b\\c") == "a.b\\c.dll"
+    }
+
+    def "windows transforms static library names"() {
+        def os = new OperatingSystem.Windows()
+
+        expect:
+        os.getStaticLibraryName("a.lib") == "a.lib"
+        os.getStaticLibraryName("a.LIB") == "a.LIB"
+        os.getStaticLibraryName("a") == "a.lib"
+        os.getStaticLibraryName("a.dll") == "a.lib"
+        os.getStaticLibraryName("a.b/c") == "a.b/c.lib"
+        os.getStaticLibraryName("a.b\\c") == "a.b\\c.lib"
     }
 
     def "windows searches for executable in path"() {
@@ -178,6 +199,18 @@ class OperatingSystemTest extends Specification {
         os.getSharedLibraryName("path/a") == "path/liba.so"
     }
 
+    def "UNIX transforms static library names"() {
+        def os = new OperatingSystem.Unix()
+
+        expect:
+        os.getStaticLibraryName("a.a") == "a.a"
+        os.getStaticLibraryName("liba.a") == "liba.a"
+        os.getStaticLibraryName("a") == "liba.a"
+        os.getStaticLibraryName("lib1") == "liblib1.a"
+        os.getStaticLibraryName("path/liba.a") == "path/liba.a"
+        os.getStaticLibraryName("path/a") == "path/liba.a"
+    }
+
     def "UNIX searches for executable in path"() {
         def exe = tmpDir.createFile("bin/a")
         tmpDir.createFile("bin2/a")
diff --git a/subprojects/base-services/src/test/groovy/org/gradle/internal/reflect/JavaReflectionUtilTest.groovy b/subprojects/base-services/src/test/groovy/org/gradle/internal/reflect/JavaReflectionUtilTest.groovy
index 94995e5..0740a60 100644
--- a/subprojects/base-services/src/test/groovy/org/gradle/internal/reflect/JavaReflectionUtilTest.groovy
+++ b/subprojects/base-services/src/test/groovy/org/gradle/internal/reflect/JavaReflectionUtilTest.groovy
@@ -14,67 +14,261 @@
  * limitations under the License.
  */
 
-package org.gradle.internal.reflect;
+package org.gradle.internal.reflect
 
-import spock.lang.Specification
+import org.gradle.api.specs.Spec
 import org.gradle.internal.UncheckedException
+import spock.lang.Specification
+
+import java.lang.annotation.Inherited
+import java.lang.annotation.Retention
+import java.lang.annotation.RetentionPolicy
+
+import static org.gradle.internal.reflect.JavaReflectionUtil.*
 
 class JavaReflectionUtilTest extends Specification {
-    def myProperties = new MyProperties()
+    JavaTestSubject myProperties = new JavaTestSubject()
+
+    def "read field"() {
+        expect:
+        readField(new JavaTestSubject(), boolean.class, "myBooleanProp")
+        readField(new JavaTestSubject(), String.class, "myProp") == "myValue"
+        readField(new JavaTestSubjectSubclass(), boolean.class, "myBooleanProp")
+        readField(new JavaTestSubjectSubclass(), String.class, "myProp") == "subclass"
+
+        when:
+        readField(new JavaTestSubject(), String.class, "myBooleanProp")
+
+        then:
+        def e = thrown UncheckedException
+        e.cause instanceof NoSuchFieldException
+
+        when:
+        readField(new JavaTestSubject(), String.class, "non existent")
+
+        then:
+        e = thrown UncheckedException
+        e.cause instanceof NoSuchFieldException
+
+        when:
+        readField(new JavaTestSubjectSubclass(), String.class, "non existent")
+
+        then:
+        e = thrown UncheckedException
+        e.cause instanceof NoSuchFieldException
+    }
+
+    def "property exists"() {
+        expect:
+        propertyExists(new JavaTestSubject(), "myBooleanProperty")
+        propertyExists(new JavaTestSubject(), "myProperty")
+        propertyExists(new JavaTestSubject(), "publicField")
+        !propertyExists(new JavaTestSubject(), "myBooleanProp")
+        !propertyExists(new JavaTestSubject(), "protectedProperty")
+        !propertyExists(new JavaTestSubject(), "privateProperty")
+
+        and:
+        propertyExists(new JavaTestSubjectSubclass(), "myBooleanProperty")
+        propertyExists(new JavaTestSubjectSubclass(), "myProperty")
+        propertyExists(new JavaTestSubjectSubclass(), "publicField")
+        !propertyExists(new JavaTestSubjectSubclass(), "myBooleanProp")
+        !propertyExists(new JavaTestSubjectSubclass(), "protectedProperty")
+        !propertyExists(new JavaTestSubjectSubclass(), "privateProperty")
+
+        and:
+        propertyExists(new JavaTestSubjectSubclass(), "subclassBoolean")
+    }
+
+    def "readable properties"() {
+        expect:
+        def properties = readableProperties(JavaTestSubjectSubclass)
+        properties.size() == 5
+        properties.class
+        properties.myProperty
+        properties.myBooleanProperty
+        properties.myOtherBooleanProperty
+        properties.subclassBoolean
+    }
 
     def "read property"() {
         expect:
-        JavaReflectionUtil.readProperty(myProperties, "myProperty") == "myValue"
+        readableProperty(JavaTestSubject, "myProperty").getValue(myProperties) == "myValue"
     }
 
     def "write property"() {
         when:
-        JavaReflectionUtil.writeProperty(myProperties, "myProperty", "otherValue")
+        writeableProperty(JavaTestSubject, "myProperty").setValue(myProperties, "otherValue")
 
         then:
-        JavaReflectionUtil.readProperty(myProperties, "myProperty") == "otherValue"
+        readableProperty(JavaTestSubject, "myProperty").getValue(myProperties) == "otherValue"
     }
 
     def "read boolean property"() {
         expect:
-        JavaReflectionUtil.readProperty(myProperties, "myBooleanProperty") == true
+        readableProperty(JavaTestSubject, "myBooleanProperty").getValue(myProperties) == true
     }
 
     def "write boolean property"() {
         when:
-        JavaReflectionUtil.writeProperty(myProperties, "myBooleanProperty", false)
+        writeableProperty(JavaTestSubject, "myBooleanProperty").setValue(myProperties, false)
+
+        then:
+        readableProperty(JavaTestSubject, "myBooleanProperty").getValue(myProperties) == false
+    }
+
+    def "read property that doesn't have a well formed getter"() {
+        when:
+        readableProperty(JavaTestSubject, property)
+
+        then:
+        NoSuchPropertyException e = thrown()
+        e.message == "Could not find getter method for property '${property}' on class JavaTestSubject."
+
+        where:
+        property              | _
+        "doesNotExist"        | _
+        "notABooleanProperty" | _
+        "staticProperty"      | _
+        "paramProperty"       | _
+        "voidProperty"        | _
+        "writeOnly"           | _
+    }
+
+    def "read property that is not public"() {
+        when:
+        readableProperty(JavaTestSubject, property)
 
         then:
-        JavaReflectionUtil.readProperty(myProperties, "myBooleanProperty") == false
+        NoSuchPropertyException e = thrown()
+        e.message == "Could not find getter method for property '${property}' on class JavaTestSubject."
+
+        where:
+        property            | _
+        "privateProperty"   | _
+        "protectedProperty" | _
     }
 
-    def "read property that doesn't exist"() {
+    def "write property that doesn't have a well formed setter"() {
         when:
-        JavaReflectionUtil.readProperty(myProperties, "unexisting")
+        writeableProperty(JavaTestSubject, property)
 
         then:
-        UncheckedException e = thrown()
-        e.cause instanceof NoSuchMethodException
+        NoSuchPropertyException e = thrown()
+        e.message == "Could not find setter method for property '${property}' on class JavaTestSubject."
+
+        where:
+        property                 | _
+        "doesNotExist"           | _
+        "myOtherBooleanProperty" | _
+        "staticProperty"         | _
+        "paramProperty"          | _
     }
 
-    def "write property that doesn't exist"() {
+    def "write property that is not public"() {
         when:
-        JavaReflectionUtil.writeProperty(myProperties, "unexisting", "someValue")
+        writeableProperty(JavaTestSubject, property)
 
         then:
-        UncheckedException e = thrown()
-        e.cause instanceof NoSuchMethodException
+        NoSuchPropertyException e = thrown()
+        e.message == "Could not find setter method for property '${property}' on class JavaTestSubject."
+
+        where:
+        property            | _
+        "privateProperty"   | _
+        "protectedProperty" | _
     }
 
-    static class MyProperties {
-        private String myProp = "myValue"
-        private boolean myBooleanProp = true
+    def "call methods successfully reflectively"() {
+        expect:
+        method(myProperties.class, String, "getMyProperty").invoke(myProperties) == myProperties.myProp
 
-        String getMyProperty() {  myProp }
-        void setMyProperty(String value) { myProp = value }
+        when:
+        method(myProperties.class, Void, "setMyProperty", String).invoke(myProperties, "foo")
 
-        boolean isMyBooleanProperty() { myBooleanProp }
-        void setMyBooleanProperty(boolean value) { myBooleanProp = value }
+        then:
+        method(myProperties.class, String, "getMyProperty").invoke(myProperties) == "foo"
     }
+
+    def "call failing methods reflectively"() {
+        when:
+        method(myProperties.class, Void, "throwsException").invoke(myProperties)
+
+        then:
+        thrown IllegalStateException
+    }
+
+    def "call declared method that may not be public"() {
+        expect:
+        method(JavaTestSubjectSubclass, String, "protectedMethod").invoke(new JavaTestSubjectSubclass()) == "parent"
+        method(JavaTestSubjectSubclass, String, "overridden").invoke(new JavaTestSubjectSubclass()) == "subclass"
+    }
+
+    def "find method"() {
+        expect:
+        findMethod(String, { it.name == "toString" } as Spec) == String.declaredMethods.find { it.name == "toString" }
+        findMethod(String, { it.name == "getClass" } as Spec) == Object.declaredMethods.find { it.name == "getClass" }
+    }
+
+    def "get annotation"() {
+        expect:
+        getAnnotation(Root, InheritedAnnotation).value() == "default"
+        getAnnotation(Subclass, InheritedAnnotation).value() == "default"
+        getAnnotation(RootInterface, InheritedAnnotation).value() == "default"
+        getAnnotation(SubInterface, InheritedAnnotation).value() == "default"
+
+        getAnnotation(Root, NotInheritedAnnotation).value() == "default"
+        getAnnotation(Subclass, NotInheritedAnnotation) == null
+        getAnnotation(RootInterface, NotInheritedAnnotation).value() == "default"
+        getAnnotation(SubInterface, NotInheritedAnnotation) == null
+
+        getAnnotation(ImplementsRootInterface, InheritedAnnotation).value() == "default"
+        getAnnotation(ImplementsRootInterface, NotInheritedAnnotation) == null
+        getAnnotation(ImplementsSubInterface, InheritedAnnotation).value() == "default"
+        getAnnotation(ImplementsSubInterface, NotInheritedAnnotation) == null
+        getAnnotation(ImplementsBoth, InheritedAnnotation).value() == "default"
+        getAnnotation(ImplementsBoth, NotInheritedAnnotation) == null
+
+        getAnnotation(OverrideFirst, InheritedAnnotation).value() == "HasAnnotations"
+        getAnnotation(OverrideLast, InheritedAnnotation).value() == "default"
+
+        getAnnotation(InheritsInterface, InheritedAnnotation).value() == "default"
+        getAnnotation(InheritsInterface, NotInheritedAnnotation) == null
+    }
+
+}
+
+ at Retention(RetentionPolicy.RUNTIME)
+ at Inherited
+ at interface InheritedAnnotation {
+    String value() default "default"
 }
 
+ at Retention(RetentionPolicy.RUNTIME)
+ at interface NotInheritedAnnotation {
+    String value() default "default"
+}
+
+ at InheritedAnnotation
+ at NotInheritedAnnotation
+class Root {}
+
+class Subclass extends Root {}
+
+ at InheritedAnnotation
+ at NotInheritedAnnotation
+interface RootInterface {}
+
+interface SubInterface extends RootInterface {}
+
+class ImplementsRootInterface implements RootInterface {}
+class ImplementsSubInterface implements SubInterface {}
+class ImplementsBoth implements RootInterface, SubInterface {}
+
+ at InheritedAnnotation(value = "HasAnnotations")
+interface HasAnnotations {}
+
+class OverrideFirst implements HasAnnotations, RootInterface, SubInterface {}
+class OverrideLast implements RootInterface, SubInterface, HasAnnotations {}
+
+class SuperWithInterface implements RootInterface {}
+class InheritsInterface extends SuperWithInterface {}
\ No newline at end of file
diff --git a/subprojects/base-services/src/test/groovy/org/gradle/internal/reflect/JavaTestSubject.java b/subprojects/base-services/src/test/groovy/org/gradle/internal/reflect/JavaTestSubject.java
new file mode 100644
index 0000000..27eb899
--- /dev/null
+++ b/subprojects/base-services/src/test/groovy/org/gradle/internal/reflect/JavaTestSubject.java
@@ -0,0 +1,99 @@
+/*
+ * Copyright 2013 the original author or authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.gradle.internal.reflect;
+
+ at SuppressWarnings("UnusedDeclaration")
+public class JavaTestSubject {
+
+    private String myProp = "myValue";
+    private boolean myBooleanProp = true;
+
+    public int publicField;
+
+    public String getMyProperty() {
+        return myProp;
+    }
+
+    public void setMyProperty(String value) {
+        myProp = value;
+    }
+
+    public boolean isMyBooleanProperty() {
+        return myBooleanProp;
+    }
+
+    public void setMyBooleanProperty(boolean value) {
+        myBooleanProp = value;
+    }
+
+    public boolean getMyOtherBooleanProperty() {
+        return true;
+    }
+
+    public String isNotABooleanProperty() {
+        return null;
+    }
+
+    public static String getStaticProperty() {
+        return null;
+    }
+
+    public static void setStaticProperty(String value) {
+    }
+
+    public void getVoidProperty() {
+    }
+
+    public String getParamProperty(String param) {
+        return null;
+    }
+
+    public void setParamProperty() {
+    }
+
+    public void setParamProperty(String param, String someOther) {
+    }
+
+    public void setWriteOnly(String param) {
+    }
+
+    public void throwsException() {
+        throw new IllegalStateException();
+    }
+
+    protected String protectedMethod() {
+        return "parent";
+    }
+
+    protected String overridden() {
+        return "parent";
+    }
+
+    protected String getProtectedProperty() {
+        return null;
+    }
+
+    protected void setProtectedProperty(String value) {
+    }
+
+    private String getPrivateProperty() {
+        return null;
+    }
+
+    private void setPrivateProperty(String value) {
+    }
+}
diff --git a/subprojects/base-services/src/test/groovy/org/gradle/internal/reflect/JavaTestSubjectSubclass.java b/subprojects/base-services/src/test/groovy/org/gradle/internal/reflect/JavaTestSubjectSubclass.java
new file mode 100644
index 0000000..f035d2f
--- /dev/null
+++ b/subprojects/base-services/src/test/groovy/org/gradle/internal/reflect/JavaTestSubjectSubclass.java
@@ -0,0 +1,31 @@
+/*
+ * Copyright 2013 the original author or authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.gradle.internal.reflect;
+
+public class JavaTestSubjectSubclass extends JavaTestSubject {
+
+    private String myProp = "subclass";
+
+    @Override
+    protected String overridden() {
+        return "subclass";
+    }
+
+    public boolean getSubclassBoolean() {
+        return false;
+    }
+}
diff --git a/subprojects/base-services/src/test/groovy/org/gradle/internal/service/ServiceLocatorTest.groovy b/subprojects/base-services/src/test/groovy/org/gradle/internal/service/ServiceLocatorTest.groovy
index 0b91e05..da0c5ec 100644
--- a/subprojects/base-services/src/test/groovy/org/gradle/internal/service/ServiceLocatorTest.groovy
+++ b/subprojects/base-services/src/test/groovy/org/gradle/internal/service/ServiceLocatorTest.groovy
@@ -25,21 +25,21 @@ class ServiceLocatorTest extends Specification {
         def serviceFile = stream('org.gradle.ImplClass')
 
         when:
-        def result = serviceLocator.findServiceImplementationClass(String.class)
+        def result = serviceLocator.findFactory(String.class).create()
 
         then:
-        result == String
-        1 * classLoader.getResource("META-INF/services/java.lang.String") >> serviceFile
+        result instanceof String
+        1 * classLoader.getResources("META-INF/services/java.lang.String") >> Collections.enumeration([serviceFile])
         1 * classLoader.loadClass('org.gradle.ImplClass') >> String
     }
 
-    def "findServiceImplementationClass() returns null when no service meta data resource available"() {
+    def "findFactory() returns null when no service meta data resource available"() {
         when:
-        def result = serviceLocator.findServiceImplementationClass(String.class)
+        def result = serviceLocator.findFactory(String.class)
 
         then:
         result == null
-        1 * classLoader.getResource("META-INF/services/java.lang.String") >> null
+        1 * classLoader.getResources("META-INF/services/java.lang.String") >> Collections.enumeration([])
     }
 
     def "wraps implementation class load failure"() {
@@ -47,13 +47,13 @@ class ServiceLocatorTest extends Specification {
         def failure = new ClassNotFoundException()
 
         when:
-        serviceLocator.findServiceImplementationClass(String.class)
+        serviceLocator.findFactory(String.class)
 
         then:
         RuntimeException e = thrown()
-        e.message == "Could not load implementation class 'org.gradle.ImplClass' for service 'java.lang.String'."
+        e.message == "Could not load implementation class 'org.gradle.ImplClass' for service 'java.lang.String' specified in resource '${serviceFile}'."
         e.cause == failure
-        1 * classLoader.getResource("META-INF/services/java.lang.String") >> serviceFile
+        1 * classLoader.getResources("META-INF/services/java.lang.String") >> Collections.enumeration([serviceFile])
         1 * classLoader.loadClass('org.gradle.ImplClass') >> { throw failure }
     }
 
@@ -64,37 +64,51 @@ class ServiceLocatorTest extends Specification {
 ''')
 
         when:
-        def result = serviceLocator.findServiceImplementationClass(String.class)
+        def result = serviceLocator.findFactory(String.class).create()
 
         then:
-        result == String
-        1 * classLoader.getResource("META-INF/services/java.lang.String") >> serviceFile
+        result instanceof String
+        1 * classLoader.getResources("META-INF/services/java.lang.String") >> Collections.enumeration([serviceFile])
         1 * classLoader.loadClass('org.gradle.ImplClass') >> String
     }
 
-    def "findServiceImplementationClass() fails when no implementation class specified in service meta data resource"() {
+    def "can locate multiple service implementations from one resource file"() {
+        def serviceFile = stream("""org.gradle.ImplClass1
+org.gradle.ImplClass2""")
+
+        when:
+        def result = serviceLocator.getAll(String.class)
+
+        then:
+        result.size() == 2
+        1 * classLoader.getResources("META-INF/services/java.lang.String") >> Collections.enumeration([serviceFile])
+        1 * classLoader.loadClass('org.gradle.ImplClass1') >> String
+        1 * classLoader.loadClass('org.gradle.ImplClass2') >> String
+    }
+
+    def "findFactory() fails when no implementation class specified in service meta data resource"() {
         def serviceFile = stream('#empty!')
 
         when:
-        serviceLocator.findServiceImplementationClass(String.class)
+        serviceLocator.findFactory(String.class)
 
         then:
         RuntimeException e = thrown()
-        e.message == "Could not determine implementation class for service 'java.lang.String'."
-        e.cause.message == "No implementation class for service 'java.lang.String' specified in resource '${serviceFile}'."
-        1 * classLoader.getResource("META-INF/services/java.lang.String") >> serviceFile
+        e.message == "Could not determine implementation class for service 'java.lang.String' specified in resource '${serviceFile}'."
+        e.cause.message == "No implementation class for service 'java.lang.String' specified."
+        1 * classLoader.getResources("META-INF/services/java.lang.String") >> Collections.enumeration([serviceFile])
     }
 
-    def "findServiceImplementationClass() fails when implementation class specified in service meta data resource is not assignable to service type"() {
+    def "findFactory() fails when implementation class specified in service meta data resource is not assignable to service type"() {
         given:
         implementationDeclared(String, Integer)
 
         when:
-        serviceLocator.findServiceImplementationClass(String)
+        serviceLocator.findFactory(String)
 
         then:
         RuntimeException e = thrown()
-        e.message == "Could not load implementation class 'java.lang.Integer' for service 'java.lang.String'."
+        e.message.startsWith("Could not load implementation class 'java.lang.Integer' for service 'java.lang.String' specified in resource '")
         e.cause.message == "Implementation class 'java.lang.Integer' is not assignable to service class 'java.lang.String'."
     }
 
@@ -128,6 +142,7 @@ class ServiceLocatorTest extends Specification {
         then:
         UnknownServiceException e = thrown()
         e.message == "Could not find meta-data resource 'META-INF/services/java.lang.CharSequence' for service 'java.lang.CharSequence'."
+        1 * classLoader.getResources("META-INF/services/java.lang.CharSequence") >> Collections.enumeration([])
     }
 
     def "getFactory() returns a factory which creates instances of implementation class"() {
@@ -152,15 +167,7 @@ class ServiceLocatorTest extends Specification {
         then:
         UnknownServiceException e = thrown()
         e.message == "Could not find meta-data resource 'META-INF/services/java.lang.CharSequence' for service 'java.lang.CharSequence'."
-    }
-
-    def stream(String contents) {
-        URLStreamHandler handler = Mock()
-        URLConnection connection = Mock()
-        URL url = new URL("custom", "host", 12, "file", handler)
-        _ * handler.openConnection(url) >> connection
-        _ * connection.getInputStream() >> new ByteArrayInputStream(contents.bytes)
-        return url
+        1 * classLoader.getResources("META-INF/services/java.lang.CharSequence") >> Collections.enumeration([])
     }
 
     def "newInstance() creates instances of implementation class"() {
@@ -173,10 +180,77 @@ class ServiceLocatorTest extends Specification {
         then:
         result instanceof String
     }
-    
+
+    def "getAll() returns an instance of each declared implementation"() {
+        def impl1 = stream("org.gradle.Impl1")
+        def impl2 = stream("org.gradle.Impl2")
+
+        when:
+        def result = serviceLocator.getAll(CharSequence)
+
+        then:
+        result.size() == 2
+        result[0] instanceof String
+        result[1] instanceof StringBuilder
+        1 * classLoader.getResources("META-INF/services/java.lang.CharSequence") >> Collections.enumeration([impl1, impl2])
+        1 * classLoader.loadClass("org.gradle.Impl1") >> String
+        1 * classLoader.loadClass("org.gradle.Impl2") >> StringBuilder
+    }
+
+    def "getAll() ignores duplicate implementation classes"() {
+        def impl1 = stream("org.gradle.Impl1")
+        def impl2 = stream("org.gradle.Impl2")
+        def impl3 = stream("org.gradle.Impl1")
+
+        when:
+        def result = serviceLocator.getAll(CharSequence)
+
+        then:
+        result.size() == 2
+        result[0] instanceof String
+        result[1] instanceof StringBuilder
+        1 * classLoader.getResources("META-INF/services/java.lang.CharSequence") >> Collections.enumeration([impl1, impl2, impl3])
+        1 * classLoader.loadClass("org.gradle.Impl1") >> String
+        1 * classLoader.loadClass("org.gradle.Impl2") >> StringBuilder
+    }
+
+    def "getAll() returns empty collection no meta-data file found for service type"() {
+        when:
+        def result = serviceLocator.getAll(CharSequence)
+
+        then:
+        result.empty
+        1 * classLoader.getResources("META-INF/services/java.lang.CharSequence") >> Collections.enumeration([])
+    }
+
+    def "servicelocator uses utf-8 encoding for reading serviceFile"() {
+        def serviceFile = stream(className)
+
+        when:
+        def result = serviceLocator.get(String.class)
+
+        then:
+        result instanceof String
+        1 * classLoader.getResources("META-INF/services/java.lang.String") >> Collections.enumeration([serviceFile])
+        1 * classLoader.loadClass(className) >> String
+
+        where:
+        className << ['org.gradle.κόσμε']
+    }
+
+
+    def stream(String contents) {
+        URLStreamHandler handler = Mock()
+        URLConnection connection = Mock()
+        URL url = new URL("custom", "host", 12, "file", handler)
+        _ * handler.openConnection(url) >> connection
+        _ * connection.getInputStream() >> new ByteArrayInputStream(contents.getBytes("UTF-8"))
+        return url
+    }
+
     def implementationDeclared(Class<?> serviceType, Class<?> implementationType) {
         def serviceFile = stream(implementationType.name)
-        _ * classLoader.getResource("META-INF/services/${serviceType.name}") >> serviceFile
+        _ * classLoader.getResources("META-INF/services/${serviceType.name}") >> Collections.enumeration([serviceFile])
         _ * classLoader.loadClass(implementationType.name) >> implementationType
     }
 }
diff --git a/subprojects/base-services/src/test/groovy/org/gradle/internal/service/SynchronizedServiceRegistryTest.groovy b/subprojects/base-services/src/test/groovy/org/gradle/internal/service/SynchronizedServiceRegistryTest.groovy
index 3623ccc..27427b5 100644
--- a/subprojects/base-services/src/test/groovy/org/gradle/internal/service/SynchronizedServiceRegistryTest.groovy
+++ b/subprojects/base-services/src/test/groovy/org/gradle/internal/service/SynchronizedServiceRegistryTest.groovy
@@ -19,9 +19,6 @@ package org.gradle.internal.service;
 
 import spock.lang.Specification
 
-/**
- * by Szczepan Faber, created at: 11/24/11
- */
 public class SynchronizedServiceRegistryTest extends Specification {
 
     def delegate = Mock(ServiceRegistry)
diff --git a/subprojects/base-services/src/test/groovy/org/gradle/util/CollectionUtilsTest.groovy b/subprojects/base-services/src/test/groovy/org/gradle/util/CollectionUtilsTest.groovy
index d9b96e2..84a2d52 100644
--- a/subprojects/base-services/src/test/groovy/org/gradle/util/CollectionUtilsTest.groovy
+++ b/subprojects/base-services/src/test/groovy/org/gradle/util/CollectionUtilsTest.groovy
@@ -94,6 +94,11 @@ class CollectionUtilsTest extends Specification {
         collect([1, 2, 3] as Object[], transformer { it * 2 }) == [2, 4, 6]
     }
 
+    def "collect iterable"() {
+        expect:
+        collect([1, 2, 3] as Iterable, transformer { it * 2 }) == [2, 4, 6]
+    }
+
     def "list stringize"() {
         expect:
         stringize([1, 2, 3]) == ["1", "2", "3"]
diff --git a/subprojects/base-services/src/test/groovy/org/gradle/util/JavaMethodTest.java b/subprojects/base-services/src/test/groovy/org/gradle/util/JavaMethodTest.java
new file mode 100644
index 0000000..225103b
--- /dev/null
+++ b/subprojects/base-services/src/test/groovy/org/gradle/util/JavaMethodTest.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.util;
+
+import org.gradle.internal.reflect.JavaReflectionUtil;
+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 static org.hamcrest.Matchers.equalTo;
+import static org.hamcrest.Matchers.sameInstance;
+import static org.junit.Assert.assertThat;
+import static org.junit.Assert.fail;
+
+ at RunWith(JMock.class)
+public class JavaMethodTest {
+    private final JUnit4Mockery context = new JUnit4Mockery();
+
+    @Test
+    public void invokesMethodOnObject() {
+        JavaMethod<CharSequence, CharSequence> method = JavaReflectionUtil.method(CharSequence.class, CharSequence.class, "subSequence", int.class, int.class);
+        assertThat(method.invoke("string", 0, 3), equalTo((CharSequence) "str"));
+    }
+    
+    @Test
+    public void propagatesExceptionThrownByMethod() {
+        final CharSequence mock = context.mock(CharSequence.class);
+        final RuntimeException failure = new RuntimeException();
+        context.checking(new Expectations() {{
+            one(mock).subSequence(0, 3);
+            will(throwException(failure));
+        }});
+
+        JavaMethod<CharSequence, CharSequence> method = JavaReflectionUtil.method(CharSequence.class, CharSequence.class, "subSequence", int.class, int.class);
+        try {
+            method.invoke(mock, 0, 3);
+            fail();
+        } catch (RuntimeException e) {
+            assertThat(e, sameInstance(failure));
+        }
+    }
+
+    @Test
+    public void canAccessProtectedMethod() {
+        final Package[] packages = new Package[0];
+        ClassLoader classLoader = new ClassLoader() {
+            @Override
+            protected Package[] getPackages() {
+                return packages;
+            }
+        };
+
+        JavaMethod<ClassLoader, Package[]> method = JavaReflectionUtil.method(ClassLoader.class, Package[].class, "getPackages");
+        assertThat(method.invoke(classLoader), sameInstance(packages));
+    }
+}
diff --git a/subprojects/core/src/test/groovy/org/gradle/util/hash/HashValueTest.groovy b/subprojects/base-services/src/test/groovy/org/gradle/util/hash/HashValueTest.groovy
similarity index 100%
rename from subprojects/core/src/test/groovy/org/gradle/util/hash/HashValueTest.groovy
rename to subprojects/base-services/src/test/groovy/org/gradle/util/hash/HashValueTest.groovy
diff --git a/subprojects/core/src/test/resources/org/gradle/util/ClassLoaderTest.txt b/subprojects/base-services/src/test/resources/org/gradle/util/ClassLoaderTest.txt
similarity index 100%
rename from subprojects/core/src/test/resources/org/gradle/util/ClassLoaderTest.txt
rename to subprojects/base-services/src/test/resources/org/gradle/util/ClassLoaderTest.txt
diff --git a/subprojects/build-comparison/build-comparison.gradle b/subprojects/build-comparison/build-comparison.gradle
index 9dd01ef..7e80092 100644
--- a/subprojects/build-comparison/build-comparison.gradle
+++ b/subprojects/build-comparison/build-comparison.gradle
@@ -15,12 +15,14 @@
  */
 
 dependencies {
-    groovy libraries.groovy
+    compile libraries.groovy
 
+    compile project(":resources")
     compile project(":core")
     compile project(":toolingApi")
     compile project(":reporting")
-    compile project(":ide") // for FileOutcomeIdentifier enum
+    compile project(':plugins')
+    compile project(':ear')
     compile libraries.guava
     compile libraries.slf4j_api
 
diff --git a/subprojects/build-comparison/src/main/groovy/org/gradle/api/plugins/buildcomparison/gradle/CompareGradleBuilds.java b/subprojects/build-comparison/src/main/groovy/org/gradle/api/plugins/buildcomparison/gradle/CompareGradleBuilds.java
index c3f8da8..ede1389 100644
--- a/subprojects/build-comparison/src/main/groovy/org/gradle/api/plugins/buildcomparison/gradle/CompareGradleBuilds.java
+++ b/subprojects/build-comparison/src/main/groovy/org/gradle/api/plugins/buildcomparison/gradle/CompareGradleBuilds.java
@@ -18,7 +18,7 @@ package org.gradle.api.plugins.buildcomparison.gradle;
 
 import org.gradle.api.*;
 import org.gradle.api.internal.file.FileResolver;
-import org.gradle.api.internal.filestore.FileStore;
+import org.gradle.internal.filestore.FileStore;
 import org.gradle.api.internal.filestore.PathNormalisingKeyFileStore;
 import org.gradle.api.logging.Logger;
 import org.gradle.api.plugins.buildcomparison.compare.internal.BuildComparisonResult;
diff --git a/subprojects/build-comparison/src/main/groovy/org/gradle/api/plugins/buildcomparison/gradle/internal/ComparableGradleBuildExecuter.java b/subprojects/build-comparison/src/main/groovy/org/gradle/api/plugins/buildcomparison/gradle/internal/ComparableGradleBuildExecuter.java
index b60d456..a969718 100644
--- a/subprojects/build-comparison/src/main/groovy/org/gradle/api/plugins/buildcomparison/gradle/internal/ComparableGradleBuildExecuter.java
+++ b/subprojects/build-comparison/src/main/groovy/org/gradle/api/plugins/buildcomparison/gradle/internal/ComparableGradleBuildExecuter.java
@@ -57,7 +57,7 @@ public class ComparableGradleBuildExecuter {
             return true;
         } else {
             // Special handling for snapshots/RCs of the minimum version
-            return version.getVersionBase().equals(PROJECT_OUTCOMES_MINIMUM_VERSION.getVersionBase());
+            return version.getBaseVersion().equals(PROJECT_OUTCOMES_MINIMUM_VERSION);
         }
     }
 
diff --git a/subprojects/build-comparison/src/main/groovy/org/gradle/api/plugins/buildcomparison/gradle/internal/DefaultGradleBuildInvocationSpec.java b/subprojects/build-comparison/src/main/groovy/org/gradle/api/plugins/buildcomparison/gradle/internal/DefaultGradleBuildInvocationSpec.java
index 8face84..77f76a4 100644
--- a/subprojects/build-comparison/src/main/groovy/org/gradle/api/plugins/buildcomparison/gradle/internal/DefaultGradleBuildInvocationSpec.java
+++ b/subprojects/build-comparison/src/main/groovy/org/gradle/api/plugins/buildcomparison/gradle/internal/DefaultGradleBuildInvocationSpec.java
@@ -59,9 +59,6 @@ public class DefaultGradleBuildInvocationSpec implements GradleBuildInvocationSp
             throw new IllegalArgumentException("gradleVersion cannot be null");
         }
         GradleVersion version = GradleVersion.version(gradleVersion);
-        if (!version.isValid()) {
-            throw new IllegalArgumentException(String.format("%s is not a valid Gradle version string (examples: '1.0', 1.0-rc-1'", gradleVersion));
-        }
         this.gradleVersion = version.getVersion();
     }
 
diff --git a/subprojects/build-comparison/src/main/groovy/org/gradle/api/plugins/buildcomparison/gradle/internal/GradleBuildComparison.java b/subprojects/build-comparison/src/main/groovy/org/gradle/api/plugins/buildcomparison/gradle/internal/GradleBuildComparison.java
index af75bac..7afc9ba 100644
--- a/subprojects/build-comparison/src/main/groovy/org/gradle/api/plugins/buildcomparison/gradle/internal/GradleBuildComparison.java
+++ b/subprojects/build-comparison/src/main/groovy/org/gradle/api/plugins/buildcomparison/gradle/internal/GradleBuildComparison.java
@@ -20,7 +20,7 @@ import org.gradle.api.Action;
 import org.gradle.api.GradleException;
 import org.gradle.api.Transformer;
 import org.gradle.api.internal.IoActions;
-import org.gradle.api.internal.filestore.FileStore;
+import org.gradle.internal.filestore.FileStore;
 import org.gradle.api.invocation.Gradle;
 import org.gradle.api.logging.Logger;
 import org.gradle.api.plugins.buildcomparison.compare.internal.*;
diff --git a/subprojects/build-comparison/src/main/groovy/org/gradle/api/plugins/buildcomparison/gradle/internal/GradleBuildOutcomeSetInferrer.java b/subprojects/build-comparison/src/main/groovy/org/gradle/api/plugins/buildcomparison/gradle/internal/GradleBuildOutcomeSetInferrer.java
index 3b15fd3..98b320a 100644
--- a/subprojects/build-comparison/src/main/groovy/org/gradle/api/plugins/buildcomparison/gradle/internal/GradleBuildOutcomeSetInferrer.java
+++ b/subprojects/build-comparison/src/main/groovy/org/gradle/api/plugins/buildcomparison/gradle/internal/GradleBuildOutcomeSetInferrer.java
@@ -17,11 +17,11 @@
 package org.gradle.api.plugins.buildcomparison.gradle.internal;
 
 import org.gradle.api.Transformer;
-import org.gradle.api.internal.filestore.FileStore;
-import org.gradle.api.internal.filestore.FileStoreEntry;
+import org.gradle.internal.filestore.FileStore;
 import org.gradle.api.plugins.buildcomparison.outcome.internal.BuildOutcome;
 import org.gradle.api.plugins.buildcomparison.outcome.internal.archive.GeneratedArchiveBuildOutcome;
 import org.gradle.api.plugins.buildcomparison.outcome.internal.unknown.UnknownBuildOutcome;
+import org.gradle.internal.resource.local.LocallyAvailableResource;
 import org.gradle.util.CollectionUtils;
 
 import java.io.File;
@@ -59,13 +59,13 @@ public class GradleBuildOutcomeSetInferrer implements Transformer<Set<BuildOutco
             // TODO - we are relying on knowledge that the name of the outcome is the task path
             String taskPath = outcome.getName();
 
-            FileStoreEntry fileStoreEntry = null;
+            LocallyAvailableResource resource = null;
             if (file.exists()) {
                 String filestoreDestination = String.format("%s/%s/%s", fileStorePrefix, taskPath, file.getName());
-                fileStoreEntry = fileStore.move(filestoreDestination, file);
+                resource = fileStore.move(filestoreDestination, file);
             }
 
-            return new GeneratedArchiveBuildOutcome(outcome.getName(), outcome.getDescription(), fileStoreEntry, rootRelativePath);
+            return new GeneratedArchiveBuildOutcome(outcome.getName(), outcome.getDescription(), resource, rootRelativePath);
         } else {
             throw new IllegalStateException(String.format("Unhandled build outcome type: %s", outcome.getClass().getName()));
         }
diff --git a/subprojects/build-comparison/src/main/groovy/org/gradle/api/plugins/buildcomparison/gradle/internal/GradleBuildOutcomeSetTransformer.java b/subprojects/build-comparison/src/main/groovy/org/gradle/api/plugins/buildcomparison/gradle/internal/GradleBuildOutcomeSetTransformer.java
index d4b3933..20765cc 100644
--- a/subprojects/build-comparison/src/main/groovy/org/gradle/api/plugins/buildcomparison/gradle/internal/GradleBuildOutcomeSetTransformer.java
+++ b/subprojects/build-comparison/src/main/groovy/org/gradle/api/plugins/buildcomparison/gradle/internal/GradleBuildOutcomeSetTransformer.java
@@ -17,12 +17,12 @@
 package org.gradle.api.plugins.buildcomparison.gradle.internal;
 
 import org.gradle.api.Transformer;
-import org.gradle.api.internal.filestore.FileStore;
-import org.gradle.api.internal.filestore.FileStoreEntry;
+import org.gradle.internal.filestore.FileStore;
 import org.gradle.api.plugins.buildcomparison.outcome.internal.BuildOutcome;
 import org.gradle.api.plugins.buildcomparison.outcome.internal.archive.GeneratedArchiveBuildOutcome;
 import org.gradle.api.plugins.buildcomparison.outcome.internal.unknown.UnknownBuildOutcome;
-import org.gradle.tooling.internal.provider.FileOutcomeIdentifier;
+import org.gradle.api.plugins.buildcomparison.outcome.internal.FileOutcomeIdentifier;
+import org.gradle.internal.resource.local.LocallyAvailableResource;
 import org.gradle.tooling.model.internal.outcomes.GradleFileBuildOutcome;
 import org.gradle.tooling.model.internal.outcomes.GradleBuildOutcome;
 import org.gradle.tooling.model.internal.outcomes.ProjectOutcomes;
@@ -79,13 +79,13 @@ public class GradleBuildOutcomeSetTransformer implements Transformer<Set<BuildOu
             File originalFile = outcome.getFile();
             String relativePath = GFileUtils.relativePath(rootProject.getProjectDirectory(), originalFile);
 
-            FileStoreEntry fileStoreEntry = null;
+            LocallyAvailableResource resource = null;
             if (originalFile.exists()) {
                 String filestoreDestination = String.format("%s/%s/%s", fileStorePrefix, outcome.getTaskPath(), originalFile.getName());
-                fileStoreEntry = fileStore.move(filestoreDestination, originalFile);
+                resource = fileStore.move(filestoreDestination, originalFile);
             }
 
-            BuildOutcome buildOutcome = new GeneratedArchiveBuildOutcome(outcome.getTaskPath(), outcome.getDescription(), fileStoreEntry, relativePath);
+            BuildOutcome buildOutcome = new GeneratedArchiveBuildOutcome(outcome.getTaskPath(), outcome.getDescription(), resource, relativePath);
             translatedOutcomes.add(buildOutcome);
         } else {
             translatedOutcomes.add(new UnknownBuildOutcome(outcome.getTaskPath(), outcome.getDescription()));
diff --git a/subprojects/build-comparison/src/main/groovy/org/gradle/api/plugins/buildcomparison/outcome/internal/FileOutcomeIdentifier.java b/subprojects/build-comparison/src/main/groovy/org/gradle/api/plugins/buildcomparison/outcome/internal/FileOutcomeIdentifier.java
new file mode 100644
index 0000000..b8a38e7
--- /dev/null
+++ b/subprojects/build-comparison/src/main/groovy/org/gradle/api/plugins/buildcomparison/outcome/internal/FileOutcomeIdentifier.java
@@ -0,0 +1,41 @@
+/*
+ * Copyright 2012 the original author or authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF 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.buildcomparison.outcome.internal;
+
+/**
+ * This should not be used as a type in the model. It's just a container for known
+ * {@link org.gradle.tooling.model.internal.outcomes.GradleFileBuildOutcome#getTypeIdentifier()} values.
+ */
+public enum FileOutcomeIdentifier {
+    ZIP_ARTIFACT("artifact.zip"),
+    JAR_ARTIFACT("artifact.jar"),
+    WAR_ARTIFACT("artifact.war"),
+    EAR_ARTIFACT("artifact.ear"),
+    TAR_ARTIFACT("artifact.tar"),
+    ARCHIVE_ARTIFACT("artifact.archive"), // We know it's an archive, but not what kind of archive
+    UNKNOWN_ARTIFACT("artifact.unknown"); // We know it's an artifact, but that's all we know for sure
+
+    private String typeIdentifier;
+
+    FileOutcomeIdentifier(String typeIdentifier) {
+        this.typeIdentifier = typeIdentifier;
+    }
+
+    public String getTypeIdentifier() {
+        return typeIdentifier;
+    }
+}
diff --git a/subprojects/build-comparison/src/main/groovy/org/gradle/api/plugins/buildcomparison/outcome/internal/archive/GeneratedArchiveBuildOutcome.java b/subprojects/build-comparison/src/main/groovy/org/gradle/api/plugins/buildcomparison/outcome/internal/archive/GeneratedArchiveBuildOutcome.java
index 357f8cd..2b6fc4b 100644
--- a/subprojects/build-comparison/src/main/groovy/org/gradle/api/plugins/buildcomparison/outcome/internal/archive/GeneratedArchiveBuildOutcome.java
+++ b/subprojects/build-comparison/src/main/groovy/org/gradle/api/plugins/buildcomparison/outcome/internal/archive/GeneratedArchiveBuildOutcome.java
@@ -16,19 +16,19 @@
 
 package org.gradle.api.plugins.buildcomparison.outcome.internal.archive;
 
-import org.gradle.api.internal.filestore.FileStoreEntry;
 import org.gradle.api.plugins.buildcomparison.outcome.internal.BuildOutcomeSupport;
+import org.gradle.internal.resource.local.LocallyAvailableResource;
 
 import java.io.File;
 
 public class GeneratedArchiveBuildOutcome extends BuildOutcomeSupport {
 
-    private final FileStoreEntry fileStoreEntry;
+    private final LocallyAvailableResource resource;
     private final String rootRelativePath;
 
-    public GeneratedArchiveBuildOutcome(String name, String description, FileStoreEntry fileStoreEntry, String rootRelativePath) {
+    public GeneratedArchiveBuildOutcome(String name, String description, LocallyAvailableResource resource, String rootRelativePath) {
         super(name, description);
-        this.fileStoreEntry = fileStoreEntry;
+        this.resource = resource;
         this.rootRelativePath = rootRelativePath;
     }
 
@@ -40,7 +40,7 @@ public class GeneratedArchiveBuildOutcome extends BuildOutcomeSupport {
      * @return The generated archive, or null if no archive was generated.
      */
     public File getArchiveFile() {
-        return fileStoreEntry == null ? null : fileStoreEntry.getFile();
+        return resource == null ? null : resource.getFile();
     }
 
     /**
diff --git a/subprojects/build-comparison/src/main/groovy/org/gradle/api/plugins/buildcomparison/outcome/internal/tooling/DefaultGradleBuildOutcome.java b/subprojects/build-comparison/src/main/groovy/org/gradle/api/plugins/buildcomparison/outcome/internal/tooling/DefaultGradleBuildOutcome.java
new file mode 100644
index 0000000..28edf41
--- /dev/null
+++ b/subprojects/build-comparison/src/main/groovy/org/gradle/api/plugins/buildcomparison/outcome/internal/tooling/DefaultGradleBuildOutcome.java
@@ -0,0 +1,46 @@
+/*
+ * Copyright 2012 the original author or authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF 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.buildcomparison.outcome.internal.tooling;
+
+import org.gradle.tooling.model.internal.outcomes.GradleBuildOutcome;
+
+import java.io.Serializable;
+
+public class DefaultGradleBuildOutcome implements GradleBuildOutcome, Serializable {
+
+    private final String id;
+    private final String description;
+    private final String taskPath;
+
+    public DefaultGradleBuildOutcome(String id, String description, String taskPath) {
+        this.id = id;
+        this.description = description;
+        this.taskPath = taskPath;
+    }
+
+    public String getId() {
+        return id;
+    }
+
+    public String getDescription() {
+        return description;
+    }
+
+    public String getTaskPath() {
+        return taskPath;
+    }
+}
diff --git a/subprojects/build-comparison/src/main/groovy/org/gradle/api/plugins/buildcomparison/outcome/internal/tooling/DefaultGradleFileBuildOutcome.java b/subprojects/build-comparison/src/main/groovy/org/gradle/api/plugins/buildcomparison/outcome/internal/tooling/DefaultGradleFileBuildOutcome.java
new file mode 100644
index 0000000..1a09506
--- /dev/null
+++ b/subprojects/build-comparison/src/main/groovy/org/gradle/api/plugins/buildcomparison/outcome/internal/tooling/DefaultGradleFileBuildOutcome.java
@@ -0,0 +1,42 @@
+/*
+ * Copyright 2012 the original author or authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF 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.buildcomparison.outcome.internal.tooling;
+
+import org.gradle.tooling.model.internal.outcomes.GradleFileBuildOutcome;
+
+import java.io.File;
+
+public class DefaultGradleFileBuildOutcome extends DefaultGradleBuildOutcome implements GradleFileBuildOutcome {
+
+    private final File file;
+    private final String typeIdentifier;
+
+    public DefaultGradleFileBuildOutcome(String id, String description, String taskPath, File file, String typeIdentifier) {
+        super(id, description, taskPath);
+        this.file = file;
+        this.typeIdentifier = typeIdentifier;
+    }
+
+    public File getFile() {
+        return file;
+    }
+
+    public String getTypeIdentifier() {
+        return typeIdentifier;
+    }
+
+}
diff --git a/subprojects/build-comparison/src/main/groovy/org/gradle/api/plugins/buildcomparison/outcome/internal/tooling/DefaultProjectOutcomes.java b/subprojects/build-comparison/src/main/groovy/org/gradle/api/plugins/buildcomparison/outcome/internal/tooling/DefaultProjectOutcomes.java
new file mode 100644
index 0000000..7e5d6fc
--- /dev/null
+++ b/subprojects/build-comparison/src/main/groovy/org/gradle/api/plugins/buildcomparison/outcome/internal/tooling/DefaultProjectOutcomes.java
@@ -0,0 +1,79 @@
+/*
+ * Copyright 2012 the original author or authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF 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.buildcomparison.outcome.internal.tooling;
+
+import com.google.common.collect.Lists;
+import org.gradle.tooling.model.DomainObjectSet;
+import org.gradle.tooling.model.internal.ImmutableDomainObjectSet;
+import org.gradle.tooling.model.internal.outcomes.GradleBuildOutcome;
+import org.gradle.tooling.model.internal.outcomes.ProjectOutcomes;
+
+import java.io.File;
+import java.io.Serializable;
+import java.util.List;
+
+public class DefaultProjectOutcomes implements ProjectOutcomes, Serializable {
+    private final String name;
+    private final String projectPath;
+    private final String description;
+    private final File projectDirectory;
+    private final DomainObjectSet<? extends GradleBuildOutcome> outcomes;
+    private final ProjectOutcomes parent;
+    private final List<ProjectOutcomes> children = Lists.newArrayList();
+
+    public DefaultProjectOutcomes(String name, String projectPath, String description, File projectDirectory,
+                                  DomainObjectSet<? extends GradleBuildOutcome> outcomes, ProjectOutcomes parent) {
+        this.name = name;
+        this.projectPath = projectPath;
+        this.description = description;
+        this.projectDirectory = projectDirectory;
+        this.outcomes = outcomes;
+        this.parent = parent;
+    }
+
+    public String getName() {
+        return name;
+    }
+
+    public String getPath() {
+        return projectPath;
+    }
+
+    public String getDescription() {
+        return description;
+    }
+
+    public File getProjectDirectory() {
+        return projectDirectory;
+    }
+
+    public DomainObjectSet<? extends GradleBuildOutcome> getOutcomes() {
+        return outcomes;
+    }
+
+    public ProjectOutcomes getParent() {
+        return parent;
+    }
+
+    public DomainObjectSet<ProjectOutcomes> getChildren() {
+        return new ImmutableDomainObjectSet<ProjectOutcomes>(children);
+    }
+
+    public void addChild(ProjectOutcomes child) {
+        children.add(child);
+    }
+}
diff --git a/subprojects/build-comparison/src/main/groovy/org/gradle/api/plugins/buildcomparison/outcome/internal/tooling/ProjectOutcomesModelBuilder.java b/subprojects/build-comparison/src/main/groovy/org/gradle/api/plugins/buildcomparison/outcome/internal/tooling/ProjectOutcomesModelBuilder.java
new file mode 100644
index 0000000..15e9481
--- /dev/null
+++ b/subprojects/build-comparison/src/main/groovy/org/gradle/api/plugins/buildcomparison/outcome/internal/tooling/ProjectOutcomesModelBuilder.java
@@ -0,0 +1,67 @@
+/*
+ * Copyright 2012 the original author or authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF 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.buildcomparison.outcome.internal.tooling;
+
+import com.google.common.collect.Lists;
+import org.gradle.api.Project;
+import org.gradle.api.artifacts.Configuration;
+import org.gradle.api.artifacts.PublishArtifact;
+import org.gradle.tooling.model.DomainObjectSet;
+import org.gradle.tooling.model.internal.ImmutableDomainObjectSet;
+import org.gradle.tooling.model.internal.outcomes.GradleFileBuildOutcome;
+import org.gradle.tooling.model.internal.outcomes.ProjectOutcomes;
+import org.gradle.tooling.provider.model.ToolingModelBuilder;
+
+import java.util.List;
+
+public class ProjectOutcomesModelBuilder implements ToolingModelBuilder {
+    private final PublishArtifactToFileBuildOutcomeTransformer artifactTransformer = new PublishArtifactToFileBuildOutcomeTransformer();
+
+    public boolean canBuild(String modelName) {
+        return modelName.equals("org.gradle.tooling.model.outcomes.ProjectOutcomes");
+    }
+
+    public Object buildAll(String modelName, Project project) {
+        return buildProjectOutput(project.getRootProject(), null);
+    }
+
+    private DefaultProjectOutcomes buildProjectOutput(Project project, ProjectOutcomes parent) {
+        DefaultProjectOutcomes projectOutput = new DefaultProjectOutcomes(project.getName(), project.getPath(),
+                project.getDescription(), project.getProjectDir(), getFileOutcomes(project), parent);
+        for (Project child : project.getChildProjects().values()) {
+            projectOutput.addChild(buildProjectOutput(child, projectOutput));
+        }
+        return projectOutput;
+    }
+
+    private DomainObjectSet<GradleFileBuildOutcome> getFileOutcomes(Project project) {
+        List<GradleFileBuildOutcome> fileBuildOutcomes = Lists.newArrayList();
+        addArtifacts(project, fileBuildOutcomes);
+        return new ImmutableDomainObjectSet<GradleFileBuildOutcome>(fileBuildOutcomes);
+    }
+
+    private void addArtifacts(Project project, List<GradleFileBuildOutcome> outcomes) {
+        Configuration configuration = project.getConfigurations().findByName("archives");
+        if (configuration != null) {
+            for (PublishArtifact artifact : configuration.getArtifacts()) {
+                GradleFileBuildOutcome outcome = artifactTransformer.transform(artifact, project);
+                outcomes.add(outcome);
+            }
+        }
+    }
+
+}
diff --git a/subprojects/build-comparison/src/main/groovy/org/gradle/api/plugins/buildcomparison/outcome/internal/tooling/PublishArtifactToFileBuildOutcomeTransformer.java b/subprojects/build-comparison/src/main/groovy/org/gradle/api/plugins/buildcomparison/outcome/internal/tooling/PublishArtifactToFileBuildOutcomeTransformer.java
new file mode 100644
index 0000000..b853a6e
--- /dev/null
+++ b/subprojects/build-comparison/src/main/groovy/org/gradle/api/plugins/buildcomparison/outcome/internal/tooling/PublishArtifactToFileBuildOutcomeTransformer.java
@@ -0,0 +1,97 @@
+/*
+ * Copyright 2012 the original author or authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF 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.buildcomparison.outcome.internal.tooling;
+
+import org.gradle.api.Project;
+import org.gradle.api.Task;
+import org.gradle.api.artifacts.PublishArtifact;
+import org.gradle.api.internal.artifacts.publish.ArchivePublishArtifact;
+import org.gradle.api.tasks.bundling.*;
+import org.gradle.plugins.ear.Ear;
+import org.gradle.tooling.model.internal.outcomes.GradleFileBuildOutcome;
+
+import java.net.URI;
+import java.util.Set;
+
+import static org.gradle.api.plugins.buildcomparison.outcome.internal.FileOutcomeIdentifier.*;
+
+public class PublishArtifactToFileBuildOutcomeTransformer {
+
+    public GradleFileBuildOutcome transform(PublishArtifact artifact, Project project) {
+        String id = getId(artifact, project);
+        String taskPath = getTaskPath(artifact);
+        String description = getDescription(artifact);
+        String typeIdentifier = getTypeIdentifier(artifact);
+
+        return new DefaultGradleFileBuildOutcome(id, description, taskPath, artifact.getFile(), typeIdentifier);
+    }
+
+    private String getId(PublishArtifact artifact, Project project) {
+        // Assume that each artifact points to a unique file, and use the relative path from the project as the id
+        URI artifactUri = artifact.getFile().toURI();
+        URI projectDirUri = project.getProjectDir().toURI();
+        URI relativeUri = projectDirUri.relativize(artifactUri);
+        return relativeUri.getPath();
+    }
+
+    private String getDescription(PublishArtifact artifact) {
+        return String.format("Publish artifact '%s'", artifact.toString());
+    }
+
+    private String getTypeIdentifier(PublishArtifact artifact) {
+        if (artifact instanceof ArchivePublishArtifact) {
+            ArchivePublishArtifact publishArtifact = (ArchivePublishArtifact) artifact;
+            AbstractArchiveTask task = publishArtifact.getArchiveTask();
+
+            // There is an inheritance hierarchy in play here, so the order
+            // of the clauses is very important.
+
+            if (task instanceof War) {
+                return WAR_ARTIFACT.getTypeIdentifier();
+            } else if (task instanceof Ear) {
+                return EAR_ARTIFACT.getTypeIdentifier();
+            } else if (task instanceof Jar) {
+                return JAR_ARTIFACT.getTypeIdentifier();
+            } else if (task instanceof Zip) {
+                return ZIP_ARTIFACT.getTypeIdentifier();
+            } else if (task instanceof Tar) {
+                return TAR_ARTIFACT.getTypeIdentifier();
+            } else {
+                // we don't know about this kind of archive task
+                return ARCHIVE_ARTIFACT.getTypeIdentifier();
+            }
+        } else {
+            // This could very well be a zip (or something else we understand), but we can't know for sure.
+            // The client may try to infer from the file extension.
+            return UNKNOWN_ARTIFACT.getTypeIdentifier();
+        }
+    }
+
+    private String getTaskPath(PublishArtifact artifact) {
+        if (artifact instanceof ArchivePublishArtifact) {
+            return ((ArchivePublishArtifact) artifact).getArchiveTask().getPath();
+        } else {
+            String taskPath = null;
+            Set<? extends Task> tasks = artifact.getBuildDependencies().getDependencies(null);
+            if (!tasks.isEmpty()) {
+                taskPath = tasks.iterator().next().getPath();
+            }
+            return taskPath;
+        }
+    }
+
+}
diff --git a/subprojects/build-comparison/src/main/groovy/org/gradle/api/plugins/buildcomparison/outcome/internal/tooling/ToolingRegistrationAction.java b/subprojects/build-comparison/src/main/groovy/org/gradle/api/plugins/buildcomparison/outcome/internal/tooling/ToolingRegistrationAction.java
new file mode 100644
index 0000000..59add9a
--- /dev/null
+++ b/subprojects/build-comparison/src/main/groovy/org/gradle/api/plugins/buildcomparison/outcome/internal/tooling/ToolingRegistrationAction.java
@@ -0,0 +1,27 @@
+/*
+ * Copyright 2012 the original author or authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF 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.buildcomparison.outcome.internal.tooling;
+
+import org.gradle.api.internal.project.ProjectInternal;
+import org.gradle.configuration.project.ProjectConfigureAction;
+import org.gradle.tooling.provider.model.ToolingModelBuilderRegistry;
+
+public class ToolingRegistrationAction implements ProjectConfigureAction {
+    public void execute(ProjectInternal project) {
+        project.getServices().get(ToolingModelBuilderRegistry.class).register(new ProjectOutcomesModelBuilder());
+    }
+}
diff --git a/subprojects/build-comparison/src/main/resources/META-INF/services/org.gradle.configuration.project.ProjectConfigureAction b/subprojects/build-comparison/src/main/resources/META-INF/services/org.gradle.configuration.project.ProjectConfigureAction
new file mode 100644
index 0000000..a2789fd
--- /dev/null
+++ b/subprojects/build-comparison/src/main/resources/META-INF/services/org.gradle.configuration.project.ProjectConfigureAction
@@ -0,0 +1 @@
+org.gradle.api.plugins.buildcomparison.outcome.internal.tooling.ToolingRegistrationAction
\ No newline at end of file
diff --git a/subprojects/build-comparison/src/test/groovy/org/gradle/api/plugins/buildcomparison/gradle/internal/DefaultGradleBuildInvocationSpecTest.groovy b/subprojects/build-comparison/src/test/groovy/org/gradle/api/plugins/buildcomparison/gradle/internal/DefaultGradleBuildInvocationSpecTest.groovy
index e64df57..4d5c4bd 100644
--- a/subprojects/build-comparison/src/test/groovy/org/gradle/api/plugins/buildcomparison/gradle/internal/DefaultGradleBuildInvocationSpecTest.groovy
+++ b/subprojects/build-comparison/src/test/groovy/org/gradle/api/plugins/buildcomparison/gradle/internal/DefaultGradleBuildInvocationSpecTest.groovy
@@ -17,12 +17,12 @@
 package org.gradle.api.plugins.buildcomparison.gradle.internal
 
 import org.gradle.api.internal.file.FileResolver
-import org.gradle.util.HelperUtil
+import org.gradle.util.TestUtil
 import spock.lang.Specification
 
 class DefaultGradleBuildInvocationSpecTest extends Specification {
 
-    FileResolver fileResolver = HelperUtil.createRootProject().fileResolver
+    FileResolver fileResolver = TestUtil.createRootProject().fileResolver
 
     def "equals and hashCode"() {
         given:
diff --git a/subprojects/build-comparison/src/test/groovy/org/gradle/api/plugins/buildcomparison/gradle/internal/GradleBuildOutcomeSetInferrerTest.groovy b/subprojects/build-comparison/src/test/groovy/org/gradle/api/plugins/buildcomparison/gradle/internal/GradleBuildOutcomeSetInferrerTest.groovy
index 19ceb76..665f6ec 100644
--- a/subprojects/build-comparison/src/test/groovy/org/gradle/api/plugins/buildcomparison/gradle/internal/GradleBuildOutcomeSetInferrerTest.groovy
+++ b/subprojects/build-comparison/src/test/groovy/org/gradle/api/plugins/buildcomparison/gradle/internal/GradleBuildOutcomeSetInferrerTest.groovy
@@ -28,7 +28,7 @@ import org.gradle.tooling.model.internal.outcomes.ProjectOutcomes
 import org.junit.Rule
 import spock.lang.Specification
 
-import static org.gradle.tooling.internal.provider.FileOutcomeIdentifier.*
+import static org.gradle.api.plugins.buildcomparison.outcome.internal.FileOutcomeIdentifier.*
 
 class GradleBuildOutcomeSetInferrerTest extends Specification {
 
diff --git a/subprojects/build-comparison/src/test/groovy/org/gradle/api/plugins/buildcomparison/gradle/internal/GradleBuildOutcomeSetTransformerTest.groovy b/subprojects/build-comparison/src/test/groovy/org/gradle/api/plugins/buildcomparison/gradle/internal/GradleBuildOutcomeSetTransformerTest.groovy
index 84529dd..b01fb24 100644
--- a/subprojects/build-comparison/src/test/groovy/org/gradle/api/plugins/buildcomparison/gradle/internal/GradleBuildOutcomeSetTransformerTest.groovy
+++ b/subprojects/build-comparison/src/test/groovy/org/gradle/api/plugins/buildcomparison/gradle/internal/GradleBuildOutcomeSetTransformerTest.groovy
@@ -17,12 +17,12 @@
 package org.gradle.api.plugins.buildcomparison.gradle.internal
 
 import org.gradle.api.Action
-import org.gradle.api.internal.filestore.AbstractFileStoreEntry
-import org.gradle.api.internal.filestore.FileStore
-import org.gradle.api.internal.filestore.FileStoreEntry
+import org.gradle.internal.filestore.FileStore
 import org.gradle.api.plugins.buildcomparison.fixtures.ProjectOutcomesBuilder
 import org.gradle.api.plugins.buildcomparison.outcome.internal.archive.GeneratedArchiveBuildOutcome
 import org.gradle.api.plugins.buildcomparison.outcome.internal.unknown.UnknownBuildOutcome
+import org.gradle.internal.resource.local.DefaultLocallyAvailableResource
+import org.gradle.internal.resource.local.LocallyAvailableResource
 import org.gradle.test.fixtures.file.TestFile
 import org.gradle.test.fixtures.file.TestNameTestDirectoryProvider
 import org.gradle.tooling.model.internal.outcomes.GradleBuildOutcome
@@ -31,32 +31,24 @@ import org.gradle.tooling.model.internal.outcomes.ProjectOutcomes
 import org.junit.Rule
 import spock.lang.Specification
 
-import static org.gradle.tooling.internal.provider.FileOutcomeIdentifier.*
+import static org.gradle.api.plugins.buildcomparison.outcome.internal.FileOutcomeIdentifier.*
 
 class GradleBuildOutcomeSetTransformerTest extends Specification {
 
     def store = new FileStore<String>() {
-        FileStoreEntry move(String key, File source) {
-            new AbstractFileStoreEntry() {
-                File getFile() {
-                    source
-                }
-            }
+        LocallyAvailableResource move(String key, File source) {
+            new DefaultLocallyAvailableResource(source)
         }
 
-        FileStoreEntry copy(String key, File source) {
-            new AbstractFileStoreEntry() {
-                File getFile() {
-                    source
-                }
-            }
+        LocallyAvailableResource copy(String key, File source) {
+            new DefaultLocallyAvailableResource(source)
         }
 
         void moveFilestore(File destination) {
             throw new UnsupportedOperationException()
         }
 
-        FileStoreEntry add(String key, Action<File> addAction) {
+        LocallyAvailableResource add(String key, Action<File> addAction) {
             throw new UnsupportedOperationException()
         }
     }
diff --git a/subprojects/build-comparison/src/test/groovy/org/gradle/api/plugins/buildcomparison/outcome/internal/archive/GeneratedArchiveBuildOutcomeComparatorTest.groovy b/subprojects/build-comparison/src/test/groovy/org/gradle/api/plugins/buildcomparison/outcome/internal/archive/GeneratedArchiveBuildOutcomeComparatorTest.groovy
index b5a71bc..0eaa388 100644
--- a/subprojects/build-comparison/src/test/groovy/org/gradle/api/plugins/buildcomparison/outcome/internal/archive/GeneratedArchiveBuildOutcomeComparatorTest.groovy
+++ b/subprojects/build-comparison/src/test/groovy/org/gradle/api/plugins/buildcomparison/outcome/internal/archive/GeneratedArchiveBuildOutcomeComparatorTest.groovy
@@ -17,10 +17,10 @@
 package org.gradle.api.plugins.buildcomparison.outcome.internal.archive
 
 import org.gradle.api.Transformer
-import org.gradle.api.internal.filestore.AbstractFileStoreEntry
 import org.gradle.api.plugins.buildcomparison.outcome.internal.DefaultBuildOutcomeAssociation
 import org.gradle.api.plugins.buildcomparison.outcome.internal.archive.entry.ArchiveEntry
 import org.gradle.api.plugins.buildcomparison.outcome.internal.archive.entry.ArchiveEntryComparison
+import org.gradle.internal.resource.local.DefaultLocallyAvailableResource
 import org.gradle.test.fixtures.file.TestNameTestDirectoryProvider
 import org.junit.Rule
 import spock.lang.Specification
@@ -121,9 +121,7 @@ class GeneratedArchiveBuildOutcomeComparatorTest extends Specification {
     }
 
     GeneratedArchiveBuildOutcome outcome(String name, File file = dir.createFile(name)) {
-        def fileStoreEntry = new AbstractFileStoreEntry() {
-            File getFile() { file }
-        }
-        new GeneratedArchiveBuildOutcome(name, name, fileStoreEntry, name)
+        def resource = new DefaultLocallyAvailableResource(file)
+        new GeneratedArchiveBuildOutcome(name, name, resource, name)
     }
 }
diff --git a/subprojects/build-comparison/src/test/groovy/org/gradle/api/plugins/buildcomparison/outcome/internal/tooling/PublishArtifactToFileBuildOutcomeTransformerTest.groovy b/subprojects/build-comparison/src/test/groovy/org/gradle/api/plugins/buildcomparison/outcome/internal/tooling/PublishArtifactToFileBuildOutcomeTransformerTest.groovy
new file mode 100644
index 0000000..ad74474
--- /dev/null
+++ b/subprojects/build-comparison/src/test/groovy/org/gradle/api/plugins/buildcomparison/outcome/internal/tooling/PublishArtifactToFileBuildOutcomeTransformerTest.groovy
@@ -0,0 +1,87 @@
+/*
+ * Copyright 2012 the original author or authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF 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.buildcomparison.outcome.internal.tooling
+
+import org.gradle.api.Task
+import org.gradle.api.artifacts.PublishArtifact
+import org.gradle.api.internal.artifacts.publish.ArchivePublishArtifact
+import org.gradle.api.plugins.buildcomparison.outcome.internal.FileOutcomeIdentifier
+import org.gradle.api.tasks.TaskDependency
+import org.gradle.plugins.ear.Ear
+import org.gradle.tooling.model.internal.outcomes.GradleFileBuildOutcome
+import spock.lang.Specification
+import spock.lang.Unroll
+import org.gradle.api.tasks.bundling.*
+
+import static org.gradle.api.plugins.buildcomparison.outcome.internal.FileOutcomeIdentifier.*
+import org.gradle.util.TestUtil
+import org.gradle.api.Project
+
+class PublishArtifactToFileBuildOutcomeTransformerTest extends Specification {
+
+    def transformer = new PublishArtifactToFileBuildOutcomeTransformer()
+
+    Project project = TestUtil.createRootProject()
+
+    @Unroll
+    "can create outcome for #taskClass archive artifact"(Class<? extends AbstractArchiveTask> taskClass, FileOutcomeIdentifier typeIdentifier) {
+        given:
+        AbstractArchiveTask task = Mock(taskClass)
+        PublishArtifact artifact = new ArchivePublishArtifact(task)
+
+        and:
+        _ * task.getArchivePath() >> project.file("file")
+
+        when:
+        GradleFileBuildOutcome outcome = transformer.transform(artifact, project)
+
+        then:
+        outcome.typeIdentifier == typeIdentifier.typeIdentifier
+        outcome.id == "file"
+
+        where:
+        taskClass           | typeIdentifier
+        Zip                 | ZIP_ARTIFACT
+        Jar                 | JAR_ARTIFACT
+        Ear                 | EAR_ARTIFACT
+        Tar                 | TAR_ARTIFACT
+        War                 | WAR_ARTIFACT
+        AbstractArchiveTask | ARCHIVE_ARTIFACT
+    }
+
+    def "can handle generic publish artifact"() {
+        given:
+        def task = Mock(Task)
+        def taskDependency = Mock(TaskDependency)
+        def artifact = Mock(PublishArtifact)
+
+        1 * taskDependency.getDependencies(null) >>> [[task] as Set]
+        1 * task.getPath() >> "path"
+        _ * artifact.getFile() >> project.file("file")
+        1 * artifact.getBuildDependencies() >> taskDependency
+
+        when:
+        def outcome = transformer.transform(artifact, project)
+
+        then:
+        outcome.typeIdentifier == UNKNOWN_ARTIFACT.typeIdentifier
+        outcome.taskPath == "path"
+        outcome.id == "file"
+    }
+
+
+}
diff --git a/subprojects/build-setup/build-setup.gradle b/subprojects/build-setup/build-setup.gradle
new file mode 100644
index 0000000..8095d3f
--- /dev/null
+++ b/subprojects/build-setup/build-setup.gradle
@@ -0,0 +1,25 @@
+/*
+ * Copyright 2013 the original author or authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT 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 {
+	compile libraries.groovy
+	compile project(':core')
+	compile project(':plugins')
+	compile project(':wrapper')
+    integTestRuntime project(':maven')
+}
+
+useTestFixtures()
+useClassycle()
diff --git a/subprojects/build-setup/src/integTest/groovy/org/gradle/buildsetup/plugins/BuildSetupPluginIntegrationTest.groovy b/subprojects/build-setup/src/integTest/groovy/org/gradle/buildsetup/plugins/BuildSetupPluginIntegrationTest.groovy
new file mode 100644
index 0000000..023c8b5
--- /dev/null
+++ b/subprojects/build-setup/src/integTest/groovy/org/gradle/buildsetup/plugins/BuildSetupPluginIntegrationTest.groovy
@@ -0,0 +1,204 @@
+/*
+ * Copyright 2013 the original author or authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+
+
+package org.gradle.buildsetup.plugins
+
+import org.gradle.buildsetup.plugins.fixtures.WrapperTestFixture
+import org.gradle.integtests.fixtures.WellBehavedPluginTest
+import org.gradle.test.fixtures.file.TestFile
+import org.hamcrest.Matcher
+
+import static org.hamcrest.Matchers.containsString
+import static org.hamcrest.Matchers.not
+
+class BuildSetupPluginIntegrationTest extends WellBehavedPluginTest {
+    final wrapper = new WrapperTestFixture(testDirectory)
+
+    @Override
+    String getMainTask() {
+        return "setupBuild"
+    }
+
+    @Override
+    String getPluginId() {
+        "build-setup"
+    }
+
+    def "setupBuild shows up on tasks overview "() {
+        when:
+        run 'tasks'
+
+        then:
+        result.output.contains "setupBuild - Initializes a new Gradle build."
+    }
+
+    def "creates a simple project when no pom file present and no type specified"() {
+        given:
+        assert !buildFile.exists()
+        assert !settingsFile.exists()
+
+        when:
+        run 'setupBuild'
+
+        then:
+        wrapper.generated()
+        buildFile.exists()
+        settingsFile.exists()
+
+        expect:
+        succeeds 'tasks'
+    }
+
+    def "build file generation is skipped when build file already exists"() {
+        given:
+        buildFile.createFile()
+
+        when:
+        run('setupBuild')
+
+        then:
+        result.assertTasksExecuted(":setupBuild")
+        result.output.contains("The build file 'build.gradle' already exists. Skipping build initialization.")
+
+        and:
+        !settingsFile.exists()
+        wrapper.notGenerated()
+    }
+
+    def "build file generation is skipped when settings file already exists"() {
+        given:
+        settingsFile.createFile()
+
+        when:
+        run('setupBuild')
+
+        then:
+        result.assertTasksExecuted(":setupBuild")
+        result.output.contains("The settings file 'settings.gradle' already exists. Skipping build initialization.")
+
+        and:
+        !buildFile.exists()
+        wrapper.notGenerated()
+    }
+
+    def "build file generation is skipped when custom build file exists"() {
+        given:
+        def customBuildScript = testDirectory.file("customBuild.gradle").createFile()
+
+        when:
+        executer.usingBuildScript(customBuildScript)
+        run('setupBuild')
+
+        then:
+        result.assertTasksExecuted(":setupBuild")
+        result.output.contains("The build file 'customBuild.gradle' already exists. Skipping build initialization.")
+
+        and:
+        !buildFile.exists()
+        !settingsFile.exists()
+        wrapper.notGenerated()
+    }
+
+    def "build file generation is skipped when part of a multi-project build with non-standard settings file location"() {
+        given:
+        def customSettings = testDirectory.file("customSettings.gradle")
+        customSettings << """
+include 'child'
+"""
+
+        when:
+        executer.usingSettingsFile(customSettings)
+        run('setupBuild')
+
+        then:
+        result.assertTasksExecuted(":setupBuild")
+        result.output.contains("This Gradle project appears to be part of an existing multi-project Gradle build. Skipping build initialization.")
+
+        and:
+        !buildFile.exists()
+        !settingsFile.exists()
+        wrapper.notGenerated()
+    }
+
+    def "pom conversion is triggered when pom and no gradle file found"() {
+        given:
+        pom()
+
+        when:
+        run('setupBuild')
+
+        then:
+        pomValuesUsed()
+    }
+
+    def "pom conversion not triggered when build type is specified"() {
+        given:
+        pom()
+
+        when:
+        succeeds('setupBuild', '--type', 'java-library')
+
+        then:
+        pomValuesNotUsed()
+    }
+
+    def "gives decent error message when triggered with unknown setupBuild-type"() {
+        when:
+        fails('setupBuild', '--type', 'some-unknown-library')
+
+        then:
+        failure.assertHasCause("The requested build setup type 'some-unknown-library' is not supported.")
+    }
+
+    private TestFile pom() {
+        file("pom.xml") << """
+      <project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
+        xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
+        <modelVersion>4.0.0</modelVersion>
+        <groupId>util</groupId>
+        <artifactId>util</artifactId>
+        <version>2.5</version>
+        <packaging>jar</packaging>
+      </project>"""
+    }
+
+    private pomValuesUsed() {
+        buildFile.assertContents(containsPomGroup())
+        buildFile.assertContents(containsPomVersion())
+        settingsFile.assertContents(containsPomArtifactId())
+    }
+
+    private pomValuesNotUsed() {
+        buildFile.assertContents(not(containsPomGroup()))
+        buildFile.assertContents(not(containsPomVersion()))
+        settingsFile.assertContents(not(containsPomArtifactId()))
+    }
+
+    private Matcher<String> containsPomGroup() {
+        containsString("group = 'util'")
+    }
+
+    private Matcher<String> containsPomVersion() {
+        containsString("version = '2.5'")
+    }
+
+    private Matcher<String> containsPomArtifactId() {
+        containsString("rootProject.name = 'util'")
+    }
+
+}
diff --git a/subprojects/build-setup/src/integTest/groovy/org/gradle/buildsetup/plugins/JavaLibrarySetupIntegrationTest.groovy b/subprojects/build-setup/src/integTest/groovy/org/gradle/buildsetup/plugins/JavaLibrarySetupIntegrationTest.groovy
new file mode 100644
index 0000000..304097e
--- /dev/null
+++ b/subprojects/build-setup/src/integTest/groovy/org/gradle/buildsetup/plugins/JavaLibrarySetupIntegrationTest.groovy
@@ -0,0 +1,76 @@
+/*
+ * Copyright 2013 the original author or authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.gradle.buildsetup.plugins
+
+import org.gradle.buildsetup.plugins.fixtures.WrapperTestFixture
+import org.gradle.integtests.fixtures.AbstractIntegrationSpec
+import org.gradle.integtests.fixtures.DefaultTestExecutionResult
+import org.gradle.integtests.fixtures.TestExecutionResult
+
+
+class JavaLibrarySetupIntegrationTest extends AbstractIntegrationSpec {
+
+    public static final String SAMPLE_LIBRARY_CLASS = "src/main/java/Library.java"
+    public static final String SAMPLE_LIBRARY_TEST_CLASS = "src/test/java/LibraryTest.java"
+
+    final wrapper = new WrapperTestFixture(testDirectory)
+
+    def "creates sample source if no source present"() {
+        when:
+        succeeds('setupBuild', '--type', 'java-library')
+
+        then:
+        file(SAMPLE_LIBRARY_CLASS).exists()
+        file(SAMPLE_LIBRARY_TEST_CLASS).exists()
+        buildFile.exists()
+        settingsFile.exists()
+        wrapper.generated()
+
+        when:
+        succeeds("build")
+
+        then:
+        TestExecutionResult testResult = new DefaultTestExecutionResult(testDirectory)
+        testResult.assertTestClassesExecuted("LibraryTest")
+        testResult.testClass("LibraryTest").assertTestPassed("testSomeLibraryMethod")
+    }
+
+    def "setupProjectLayout is skipped when sources detected"() {
+        setup:
+        file("src/main/java/org/acme/SampleMain.java") << """
+        package org.acme;
+
+        public class SampleMain{
+        }
+"""
+        file("src/test/java/org/acme/SampleMainTest.java") << """
+                package org.acme;
+
+                public class SampleMain{
+                }
+        """
+        when:
+        succeeds('setupBuild', '--type', 'java-library')
+
+        then:
+        !file(SAMPLE_LIBRARY_CLASS).exists()
+        !file(SAMPLE_LIBRARY_TEST_CLASS).exists()
+        buildFile.exists()
+        settingsFile.exists()
+        wrapper.generated()
+    }
+}
diff --git a/subprojects/build-setup/src/integTest/groovy/org/gradle/buildsetup/plugins/MavenConversionIntegrationTest.groovy b/subprojects/build-setup/src/integTest/groovy/org/gradle/buildsetup/plugins/MavenConversionIntegrationTest.groovy
new file mode 100644
index 0000000..58d0446
--- /dev/null
+++ b/subprojects/build-setup/src/integTest/groovy/org/gradle/buildsetup/plugins/MavenConversionIntegrationTest.groovy
@@ -0,0 +1,202 @@
+/*
+ * Copyright 2012 the original author or authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.gradle.buildsetup.plugins
+
+import org.gradle.buildsetup.plugins.fixtures.WrapperTestFixture
+import org.gradle.integtests.fixtures.AbstractIntegrationSpec
+import org.gradle.integtests.fixtures.DefaultTestExecutionResult
+import org.gradle.integtests.fixtures.TestResources
+import org.gradle.test.fixtures.file.TestFile
+import org.junit.Rule
+
+import static org.gradle.util.TextUtil.toPlatformLineSeparators
+
+class MavenConversionIntegrationTest extends AbstractIntegrationSpec {
+
+    @Rule
+    public final TestResources resources = new TestResources(temporaryFolder)
+
+    def "multiModule"() {
+        when:
+        run 'setupBuild'
+
+        then:
+        settingsFile.exists()
+        buildFile.exists()
+        wrapperFilesGenerated()
+
+        when:
+        run 'clean', 'build'
+
+        then: //smoke test the build artifacts
+        file("webinar-api/build/libs/webinar-api-1.0-SNAPSHOT.jar").exists()
+        file("webinar-impl/build/libs/webinar-impl-1.0-SNAPSHOT.jar").exists()
+        file("webinar-war/build/libs/webinar-war-1.0-SNAPSHOT.war").exists()
+
+        new DefaultTestExecutionResult(file("webinar-impl")).assertTestClassesExecuted('webinar.WebinarTest')
+
+        when:
+        run 'projects'
+
+        then:
+        output.contains(toPlatformLineSeparators("""
+Root project 'webinar-parent'
++--- Project ':webinar-api' - Webinar APIs
++--- Project ':webinar-impl' - Webinar implementation
+\\--- Project ':webinar-war' - Webinar web application
+"""))
+    }
+
+    def "flatmultimodule"() {
+        when:
+        executer.inDirectory(file("webinar-parent"))
+        run 'setupBuild'
+
+        then:
+        file("webinar-parent/settings.gradle").exists()
+        file("webinar-parent/build.gradle").exists()
+        wrapperFilesGenerated(file("webinar-parent"))
+
+        when:
+        executer.inDirectory(file("webinar-parent"))
+        run 'clean', 'build'
+
+        then: //smoke test the build artifacts
+        file("webinar-api/build/libs/webinar-api-1.0-SNAPSHOT.jar").exists()
+        file("webinar-impl/build/libs/webinar-impl-1.0-SNAPSHOT.jar").exists()
+        file("webinar-war/build/libs/webinar-war-1.0-SNAPSHOT.war").exists()
+
+        new DefaultTestExecutionResult(file("webinar-impl")).assertTestClassesExecuted('webinar.WebinarTest')
+
+        when:
+        executer.inDirectory(file("webinar-parent"))
+        run 'projects'
+
+        then:
+        output.contains(toPlatformLineSeparators("""
+Root project 'webinar-parent'
++--- Project ':webinar-api' - Webinar APIs
++--- Project ':webinar-impl' - Webinar implementation
+\\--- Project ':webinar-war' - Webinar web application
+"""))
+    }
+
+    def "singleModule"() {
+        when:
+        run 'setupBuild'
+
+        then:
+        buildFile.exists()
+        settingsFile.exists()
+        wrapperFilesGenerated()
+
+        when:
+        //TODO this build should fail because the TestNG test is failing
+        //however the plugin does not generate testNG for single module project atm (bug)
+        //def failure = runAndFail('clean', 'build')  //assert if fails for the right reason
+        run 'clean', 'build'
+
+        then:
+        file("build/libs/util-2.5.jar").exists()
+    }
+
+    def "testjar"() {
+        when:
+        run 'setupBuild'
+
+        then:
+        settingsFile.exists()
+        settingsFile.exists()
+        wrapperFilesGenerated()
+
+        when:
+        run 'clean', 'build'
+
+        then:
+        file("build/libs/testjar-2.5.jar").exists()
+        file("build/libs/testjar-2.5-tests.jar").exists()
+    }
+
+    def "enforcerplugin"() {
+        when:
+        run 'setupBuild'
+
+        then:
+        settingsFile.exists()
+        buildFile.exists()
+        wrapperFilesGenerated()
+
+        and:
+        buildFile.text.contains("""configurations.all {
+it.exclude group: 'org.apache.maven'
+it.exclude group: 'org.apache.maven', module: 'badArtifact'
+it.exclude group: '*', module: 'badArtifact'
+}""")
+        when:
+        run 'clean', 'build'
+
+        then:
+        file("build/libs/enforcerExample-1.0.jar").exists()
+    }
+
+    def "providedNotWar"() {
+        when:
+        run 'setupBuild'
+
+        then:
+        settingsFile.exists()
+        buildFile.exists()
+        wrapperFilesGenerated()
+
+        when:
+        run 'clean', 'build'
+
+        then:
+        file("build/libs/myThing-0.0.1-SNAPSHOT.jar").exists()
+    }
+
+    def "provides decent error message when POM is invalid"() {
+        setup:
+        def pom = file("pom.xml")
+        pom << "<project>someInvalid pom content</project>"
+
+        when:
+        fails 'setupBuild'
+
+        then:
+        failure.assertHasCause("Could not convert Maven POM $pom to a Gradle build.")
+    }
+
+    def "mavenExtensions"() {
+        when:
+        run 'setupBuild'
+        then:
+        settingsFile.exists()
+        buildFile.exists()
+        wrapperFilesGenerated()
+
+        when:
+        run 'clean', 'build'
+
+        then:
+        file("build/libs/testApp-1.0.jar").exists()
+    }
+
+    void wrapperFilesGenerated(TestFile parentFolder = file(".")) {
+        new WrapperTestFixture(parentFolder).generated()
+    }
+}
\ No newline at end of file
diff --git a/subprojects/build-setup/src/integTest/groovy/org/gradle/buildsetup/plugins/WrapperPluginIntegrationTest.groovy b/subprojects/build-setup/src/integTest/groovy/org/gradle/buildsetup/plugins/WrapperPluginIntegrationTest.groovy
new file mode 100644
index 0000000..757229a
--- /dev/null
+++ b/subprojects/build-setup/src/integTest/groovy/org/gradle/buildsetup/plugins/WrapperPluginIntegrationTest.groovy
@@ -0,0 +1,35 @@
+/*
+ * Copyright 2013 the original author or authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.gradle.buildsetup.plugins
+
+import org.gradle.buildsetup.plugins.fixtures.WrapperTestFixture
+import org.gradle.integtests.fixtures.WellBehavedPluginTest
+
+class WrapperPluginIntegrationTest extends WellBehavedPluginTest {
+    @Override
+    String getMainTask() {
+        return "wrapper"
+    }
+
+    def "wrapper task generates wrapper files"() {
+        when:
+        run 'wrapper'
+
+        then:
+        new WrapperTestFixture(testDirectory).generated()
+    }
+}
\ No newline at end of file
diff --git a/subprojects/build-setup/src/integTest/groovy/org/gradle/buildsetup/plugins/fixtures/WrapperTestFixture.groovy b/subprojects/build-setup/src/integTest/groovy/org/gradle/buildsetup/plugins/fixtures/WrapperTestFixture.groovy
new file mode 100644
index 0000000..03731f9
--- /dev/null
+++ b/subprojects/build-setup/src/integTest/groovy/org/gradle/buildsetup/plugins/fixtures/WrapperTestFixture.groovy
@@ -0,0 +1,50 @@
+/*
+ * Copyright 2013 the original author or authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.gradle.buildsetup.plugins.fixtures
+
+import org.gradle.test.fixtures.file.TestFile
+import org.gradle.util.GradleVersion
+
+
+class WrapperTestFixture {
+
+    public static final String GRADLEW_BASH_SCRIPT = "gradlew"
+    public static final String GRADLEW_BATCH_SCRIPT = "gradlew.bat"
+    public static final String GRADLEW_WRAPPER_JAR = "gradle/wrapper/gradle-wrapper.jar"
+    public static final String GRADLEW_PROPERTY_FILE = "gradle/wrapper/gradle-wrapper.properties"
+
+    private final TestFile projectDirectory
+
+    WrapperTestFixture(TestFile projectdirectory) {
+        this.projectDirectory = projectdirectory
+    }
+
+    public void generated(String version = GradleVersion.current().version) {
+        projectDirectory.file(GRADLEW_BASH_SCRIPT).assertExists()
+        projectDirectory.file(GRADLEW_BATCH_SCRIPT).assertExists()
+        projectDirectory.file(GRADLEW_WRAPPER_JAR).assertExists()
+        projectDirectory.file(GRADLEW_PROPERTY_FILE).assertExists()
+        projectDirectory.file(GRADLEW_PROPERTY_FILE).text.contains("gradle-${version}-bin.zip")
+    }
+
+    public void notGenerated() {
+        projectDirectory.file(GRADLEW_BASH_SCRIPT).assertDoesNotExist()
+        projectDirectory.file(GRADLEW_BATCH_SCRIPT).assertDoesNotExist()
+        projectDirectory.file(GRADLEW_WRAPPER_JAR).assertDoesNotExist()
+        projectDirectory.file(GRADLEW_PROPERTY_FILE).assertDoesNotExist()
+    }
+}
diff --git a/subprojects/build-setup/src/integTest/groovy/org/gradle/buildsetup/plugins/internal/WrapperPluginAutoApplyActionIntegTest.groovy b/subprojects/build-setup/src/integTest/groovy/org/gradle/buildsetup/plugins/internal/WrapperPluginAutoApplyActionIntegTest.groovy
new file mode 100644
index 0000000..0117dda
--- /dev/null
+++ b/subprojects/build-setup/src/integTest/groovy/org/gradle/buildsetup/plugins/internal/WrapperPluginAutoApplyActionIntegTest.groovy
@@ -0,0 +1,99 @@
+/*
+ * Copyright 2013 the original author or authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.gradle.buildsetup.plugins.internal
+
+import org.gradle.buildsetup.plugins.fixtures.WrapperTestFixture
+import org.gradle.integtests.fixtures.AbstractIntegrationSpec
+import org.gradle.test.fixtures.file.TestFile
+
+class WrapperPluginAutoApplyActionIntegTest extends AbstractIntegrationSpec {
+    final wrapper = new WrapperTestFixture(testDirectory)
+
+    def "can apply wrapper plugin dynamically"() {
+        when:
+        run 'tasks'
+        then:
+        output.contains("wrapper - Generates Gradle wrapper files.")
+
+        when:
+        run 'wrapper'
+        then:
+        wrapper.generated()
+    }
+
+    def "can use camel-case for dynamically applied wrapper plugin "() {
+        when:
+        run taskName
+        then:
+        wrapper.generated()
+        where:
+        taskName << ["wrapp"]//, "wrap", "w"]
+    }
+
+    def "wrapper plugin not applied on subprojects"() {
+        setup:
+        settingsFile << "include 'moduleA'"
+        TestFile subprojectsDir = file("moduleA").createDir()
+
+        when:
+        executer.inDirectory(subprojectsDir)
+        run 'tasks'
+        then:
+        !output.contains("wrapper - Generates Gradle wrapper files.")
+
+        when:
+        executer.inDirectory(subprojectsDir)
+        then:
+        fails("wrapper")
+    }
+
+    def "can reference dynamic applied wrapper plugin"() {
+        when:
+        buildFile << """
+            wrapper{
+                gradleVersion = '12.34'
+            }
+    """
+        run 'wrapper'
+        then:
+        wrapper.generated("12.34")
+    }
+
+    def "can depend on dynamic applied wrapper task"() {
+        when:
+        buildFile << """
+            task myTask(dependsOn:wrapper)
+        """
+        run 'myTask'
+        then:
+        wrapper.generated()
+    }
+
+    def "manually declared wrapper task is preferred"() {
+        when:
+        buildFile << """
+
+                task wrapper << {
+                    println "running custom wrapper task"
+                }
+            """
+        run 'wrapper'
+        then:
+        output.contains("running custom wrapper task")
+        wrapper.notGenerated()
+    }
+}
diff --git a/subprojects/maven/src/integTest/resources/org/gradle/api/plugins/maven/MavenConversionIntegrationTest/enforcerplugin/pom.xml b/subprojects/build-setup/src/integTest/resources/org/gradle/buildsetup/plugins/MavenConversionIntegrationTest/enforcerplugin/pom.xml
similarity index 100%
rename from subprojects/maven/src/integTest/resources/org/gradle/api/plugins/maven/MavenConversionIntegrationTest/enforcerplugin/pom.xml
rename to subprojects/build-setup/src/integTest/resources/org/gradle/buildsetup/plugins/MavenConversionIntegrationTest/enforcerplugin/pom.xml
diff --git a/subprojects/maven/src/integTest/resources/org/gradle/api/plugins/maven/MavenConversionIntegrationTest/enforcerplugin/src/main/java/Foo.java b/subprojects/build-setup/src/integTest/resources/org/gradle/buildsetup/plugins/MavenConversionIntegrationTest/enforcerplugin/src/main/java/Foo.java
similarity index 100%
rename from subprojects/maven/src/integTest/resources/org/gradle/api/plugins/maven/MavenConversionIntegrationTest/enforcerplugin/src/main/java/Foo.java
rename to subprojects/build-setup/src/integTest/resources/org/gradle/buildsetup/plugins/MavenConversionIntegrationTest/enforcerplugin/src/main/java/Foo.java
diff --git a/subprojects/maven/src/integTest/resources/org/gradle/api/plugins/maven/MavenConversionIntegrationTest/flatmultimodule/webinar-api/pom.xml b/subprojects/build-setup/src/integTest/resources/org/gradle/buildsetup/plugins/MavenConversionIntegrationTest/flatmultimodule/webinar-api/pom.xml
similarity index 100%
rename from subprojects/maven/src/integTest/resources/org/gradle/api/plugins/maven/MavenConversionIntegrationTest/flatmultimodule/webinar-api/pom.xml
rename to subprojects/build-setup/src/integTest/resources/org/gradle/buildsetup/plugins/MavenConversionIntegrationTest/flatmultimodule/webinar-api/pom.xml
diff --git a/subprojects/maven/src/integTest/resources/org/gradle/api/plugins/maven/MavenConversionIntegrationTest/flatmultimodule/webinar-api/src/main/java/webinar/Demoable.java b/subprojects/build-setup/src/integTest/resources/org/gradle/buildsetup/plugins/MavenConversionIntegrationTest/flatmultimodule/webinar-api/src/main/java/webinar/Demoable.java
similarity index 100%
rename from subprojects/maven/src/integTest/resources/org/gradle/api/plugins/maven/MavenConversionIntegrationTest/flatmultimodule/webinar-api/src/main/java/webinar/Demoable.java
rename to subprojects/build-setup/src/integTest/resources/org/gradle/buildsetup/plugins/MavenConversionIntegrationTest/flatmultimodule/webinar-api/src/main/java/webinar/Demoable.java
diff --git a/subprojects/maven/src/integTest/resources/org/gradle/api/plugins/maven/MavenConversionIntegrationTest/flatmultimodule/webinar-impl/pom.xml b/subprojects/build-setup/src/integTest/resources/org/gradle/buildsetup/plugins/MavenConversionIntegrationTest/flatmultimodule/webinar-impl/pom.xml
similarity index 100%
rename from subprojects/maven/src/integTest/resources/org/gradle/api/plugins/maven/MavenConversionIntegrationTest/flatmultimodule/webinar-impl/pom.xml
rename to subprojects/build-setup/src/integTest/resources/org/gradle/buildsetup/plugins/MavenConversionIntegrationTest/flatmultimodule/webinar-impl/pom.xml
diff --git a/subprojects/maven/src/integTest/resources/org/gradle/api/plugins/maven/MavenConversionIntegrationTest/flatmultimodule/webinar-impl/src/main/java/webinar/Webinar.java b/subprojects/build-setup/src/integTest/resources/org/gradle/buildsetup/plugins/MavenConversionIntegrationTest/flatmultimodule/webinar-impl/src/main/java/webinar/Webinar.java
similarity index 100%
rename from subprojects/maven/src/integTest/resources/org/gradle/api/plugins/maven/MavenConversionIntegrationTest/flatmultimodule/webinar-impl/src/main/java/webinar/Webinar.java
rename to subprojects/build-setup/src/integTest/resources/org/gradle/buildsetup/plugins/MavenConversionIntegrationTest/flatmultimodule/webinar-impl/src/main/java/webinar/Webinar.java
diff --git a/subprojects/maven/src/integTest/resources/org/gradle/api/plugins/maven/MavenConversionIntegrationTest/flatmultimodule/webinar-impl/src/test/java/webinar/WebinarTest.java b/subprojects/build-setup/src/integTest/resources/org/gradle/buildsetup/plugins/MavenConversionIntegrationTest/flatmultimodule/webinar-impl/src/test/java/webinar/WebinarTest.java
similarity index 100%
rename from subprojects/maven/src/integTest/resources/org/gradle/api/plugins/maven/MavenConversionIntegrationTest/flatmultimodule/webinar-impl/src/test/java/webinar/WebinarTest.java
rename to subprojects/build-setup/src/integTest/resources/org/gradle/buildsetup/plugins/MavenConversionIntegrationTest/flatmultimodule/webinar-impl/src/test/java/webinar/WebinarTest.java
diff --git a/subprojects/maven/src/integTest/resources/org/gradle/api/plugins/maven/MavenConversionIntegrationTest/flatmultimodule/webinar-parent/pom.xml b/subprojects/build-setup/src/integTest/resources/org/gradle/buildsetup/plugins/MavenConversionIntegrationTest/flatmultimodule/webinar-parent/pom.xml
similarity index 100%
rename from subprojects/maven/src/integTest/resources/org/gradle/api/plugins/maven/MavenConversionIntegrationTest/flatmultimodule/webinar-parent/pom.xml
rename to subprojects/build-setup/src/integTest/resources/org/gradle/buildsetup/plugins/MavenConversionIntegrationTest/flatmultimodule/webinar-parent/pom.xml
diff --git a/subprojects/maven/src/integTest/resources/org/gradle/api/plugins/maven/MavenConversionIntegrationTest/flatmultimodule/webinar-war/pom.xml b/subprojects/build-setup/src/integTest/resources/org/gradle/buildsetup/plugins/MavenConversionIntegrationTest/flatmultimodule/webinar-war/pom.xml
similarity index 100%
rename from subprojects/maven/src/integTest/resources/org/gradle/api/plugins/maven/MavenConversionIntegrationTest/flatmultimodule/webinar-war/pom.xml
rename to subprojects/build-setup/src/integTest/resources/org/gradle/buildsetup/plugins/MavenConversionIntegrationTest/flatmultimodule/webinar-war/pom.xml
diff --git a/subprojects/maven/src/integTest/resources/org/gradle/api/plugins/maven/MavenConversionIntegrationTest/flatmultimodule/webinar-war/src/main/webapp/WEB-INF/web.xml b/subprojects/build-setup/src/integTest/resources/org/gradle/buildsetup/plugins/MavenConversionIntegrationTest/flatmultimodule/webinar-war/src/main/webapp/WEB-INF/web.xml
similarity index 100%
rename from subprojects/maven/src/integTest/resources/org/gradle/api/plugins/maven/MavenConversionIntegrationTest/flatmultimodule/webinar-war/src/main/webapp/WEB-INF/web.xml
rename to subprojects/build-setup/src/integTest/resources/org/gradle/buildsetup/plugins/MavenConversionIntegrationTest/flatmultimodule/webinar-war/src/main/webapp/WEB-INF/web.xml
diff --git a/subprojects/maven/src/integTest/resources/org/gradle/api/plugins/maven/MavenConversionIntegrationTest/flatmultimodule/webinar-war/src/main/webapp/index.jsp b/subprojects/build-setup/src/integTest/resources/org/gradle/buildsetup/plugins/MavenConversionIntegrationTest/flatmultimodule/webinar-war/src/main/webapp/index.jsp
similarity index 100%
rename from subprojects/maven/src/integTest/resources/org/gradle/api/plugins/maven/MavenConversionIntegrationTest/flatmultimodule/webinar-war/src/main/webapp/index.jsp
rename to subprojects/build-setup/src/integTest/resources/org/gradle/buildsetup/plugins/MavenConversionIntegrationTest/flatmultimodule/webinar-war/src/main/webapp/index.jsp
diff --git a/subprojects/build-setup/src/integTest/resources/org/gradle/buildsetup/plugins/MavenConversionIntegrationTest/mavenExtensions/pom.xml b/subprojects/build-setup/src/integTest/resources/org/gradle/buildsetup/plugins/MavenConversionIntegrationTest/mavenExtensions/pom.xml
new file mode 100644
index 0000000..d4aa30d
--- /dev/null
+++ b/subprojects/build-setup/src/integTest/resources/org/gradle/buildsetup/plugins/MavenConversionIntegrationTest/mavenExtensions/pom.xml
@@ -0,0 +1,47 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
+  This is the base POM for all dependent and inherited POMS of the DPJW application
+-->
+<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
+         xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/maven-v4_0_0.xsd">
+    <modelVersion>4.0.0</modelVersion>
+    <groupId>org.test</groupId>
+    <artifactId>testApp</artifactId>
+    <version>1.0</version>
+    <packaging>pom</packaging>
+    <name>A Test Project</name>
+    <description>Some Test project</description>
+    <build>
+        <plugins>
+            <!-- set this project compiler to jdk version 1.6 -->
+            <plugin>
+                <artifactId>maven-compiler-plugin</artifactId>
+                <configuration>
+                    <source>1.6</source>
+                    <target>1.6</target>
+                </configuration>
+
+            </plugin>
+            <!-- the test plugin -->
+            <plugin>
+                <groupId>org.apache.maven.plugins</groupId>
+                <artifactId>maven-surefire-plugin</artifactId>
+                <configuration>
+                    <excludes>
+                        <exclude>**/HistoryConfigurationTest.java</exclude>
+                        <exclude>**/TestDataFactory.java</exclude>
+                    </excludes>
+                    <forkMode>once</forkMode>
+                    <reportFormat>xml</reportFormat>
+                </configuration>
+            </plugin>
+        </plugins>
+        <extensions>
+            <extension>
+                <groupId>mysql</groupId>
+                <artifactId>mysql-connector-java</artifactId>
+                <version>5.0.7</version>
+            </extension>
+        </extensions>
+    </build>
+</project>
\ No newline at end of file
diff --git a/subprojects/build-setup/src/integTest/resources/org/gradle/buildsetup/plugins/MavenConversionIntegrationTest/mavenExtensions/test-core/pom.xml b/subprojects/build-setup/src/integTest/resources/org/gradle/buildsetup/plugins/MavenConversionIntegrationTest/mavenExtensions/test-core/pom.xml
new file mode 100755
index 0000000..45f4d9c
--- /dev/null
+++ b/subprojects/build-setup/src/integTest/resources/org/gradle/buildsetup/plugins/MavenConversionIntegrationTest/mavenExtensions/test-core/pom.xml
@@ -0,0 +1,22 @@
+<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
+         xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/maven-v4_0_0.xsd">
+  <modelVersion>4.0.0</modelVersion>
+  <parent>
+    <groupId>org.test</groupId>
+    <artifactId>testApp</artifactId>
+    <version>1.0</version>
+  </parent>
+
+  <artifactId>test-core</artifactId>
+  <name>test-core</name>
+  <packaging>jar</packaging>
+  <build>
+    <extensions>
+      <extension>
+        <groupId>mysql</groupId>
+        <artifactId>mysql-connector-java</artifactId>
+        <version>5.0.7</version>
+      </extension>
+    </extensions>
+  </build>
+</project>
diff --git a/subprojects/maven/src/integTest/resources/org/gradle/api/plugins/maven/MavenConversionIntegrationTest/multiModule/pom.xml b/subprojects/build-setup/src/integTest/resources/org/gradle/buildsetup/plugins/MavenConversionIntegrationTest/multiModule/pom.xml
similarity index 100%
rename from subprojects/maven/src/integTest/resources/org/gradle/api/plugins/maven/MavenConversionIntegrationTest/multiModule/pom.xml
rename to subprojects/build-setup/src/integTest/resources/org/gradle/buildsetup/plugins/MavenConversionIntegrationTest/multiModule/pom.xml
diff --git a/subprojects/maven/src/integTest/resources/org/gradle/api/plugins/maven/MavenConversionIntegrationTest/multiModule/webinar-api/pom.xml b/subprojects/build-setup/src/integTest/resources/org/gradle/buildsetup/plugins/MavenConversionIntegrationTest/multiModule/webinar-api/pom.xml
similarity index 100%
rename from subprojects/maven/src/integTest/resources/org/gradle/api/plugins/maven/MavenConversionIntegrationTest/multiModule/webinar-api/pom.xml
rename to subprojects/build-setup/src/integTest/resources/org/gradle/buildsetup/plugins/MavenConversionIntegrationTest/multiModule/webinar-api/pom.xml
diff --git a/subprojects/maven/src/integTest/resources/org/gradle/api/plugins/maven/MavenConversionIntegrationTest/multiModule/webinar-api/src/main/java/webinar/Demoable.java b/subprojects/build-setup/src/integTest/resources/org/gradle/buildsetup/plugins/MavenConversionIntegrationTest/multiModule/webinar-api/src/main/java/webinar/Demoable.java
similarity index 100%
rename from subprojects/maven/src/integTest/resources/org/gradle/api/plugins/maven/MavenConversionIntegrationTest/multiModule/webinar-api/src/main/java/webinar/Demoable.java
rename to subprojects/build-setup/src/integTest/resources/org/gradle/buildsetup/plugins/MavenConversionIntegrationTest/multiModule/webinar-api/src/main/java/webinar/Demoable.java
diff --git a/subprojects/maven/src/integTest/resources/org/gradle/api/plugins/maven/MavenConversionIntegrationTest/multiModule/webinar-impl/pom.xml b/subprojects/build-setup/src/integTest/resources/org/gradle/buildsetup/plugins/MavenConversionIntegrationTest/multiModule/webinar-impl/pom.xml
similarity index 100%
rename from subprojects/maven/src/integTest/resources/org/gradle/api/plugins/maven/MavenConversionIntegrationTest/multiModule/webinar-impl/pom.xml
rename to subprojects/build-setup/src/integTest/resources/org/gradle/buildsetup/plugins/MavenConversionIntegrationTest/multiModule/webinar-impl/pom.xml
diff --git a/subprojects/maven/src/integTest/resources/org/gradle/api/plugins/maven/MavenConversionIntegrationTest/multiModule/webinar-impl/src/main/java/webinar/Webinar.java b/subprojects/build-setup/src/integTest/resources/org/gradle/buildsetup/plugins/MavenConversionIntegrationTest/multiModule/webinar-impl/src/main/java/webinar/Webinar.java
similarity index 100%
rename from subprojects/maven/src/integTest/resources/org/gradle/api/plugins/maven/MavenConversionIntegrationTest/multiModule/webinar-impl/src/main/java/webinar/Webinar.java
rename to subprojects/build-setup/src/integTest/resources/org/gradle/buildsetup/plugins/MavenConversionIntegrationTest/multiModule/webinar-impl/src/main/java/webinar/Webinar.java
diff --git a/subprojects/maven/src/integTest/resources/org/gradle/api/plugins/maven/MavenConversionIntegrationTest/multiModule/webinar-impl/src/test/java/webinar/WebinarTest.java b/subprojects/build-setup/src/integTest/resources/org/gradle/buildsetup/plugins/MavenConversionIntegrationTest/multiModule/webinar-impl/src/test/java/webinar/WebinarTest.java
similarity index 100%
rename from subprojects/maven/src/integTest/resources/org/gradle/api/plugins/maven/MavenConversionIntegrationTest/multiModule/webinar-impl/src/test/java/webinar/WebinarTest.java
rename to subprojects/build-setup/src/integTest/resources/org/gradle/buildsetup/plugins/MavenConversionIntegrationTest/multiModule/webinar-impl/src/test/java/webinar/WebinarTest.java
diff --git a/subprojects/maven/src/integTest/resources/org/gradle/api/plugins/maven/MavenConversionIntegrationTest/multiModule/webinar-war/pom.xml b/subprojects/build-setup/src/integTest/resources/org/gradle/buildsetup/plugins/MavenConversionIntegrationTest/multiModule/webinar-war/pom.xml
similarity index 100%
rename from subprojects/maven/src/integTest/resources/org/gradle/api/plugins/maven/MavenConversionIntegrationTest/multiModule/webinar-war/pom.xml
rename to subprojects/build-setup/src/integTest/resources/org/gradle/buildsetup/plugins/MavenConversionIntegrationTest/multiModule/webinar-war/pom.xml
diff --git a/subprojects/maven/src/integTest/resources/org/gradle/api/plugins/maven/MavenConversionIntegrationTest/multiModule/webinar-war/src/main/webapp/WEB-INF/web.xml b/subprojects/build-setup/src/integTest/resources/org/gradle/buildsetup/plugins/MavenConversionIntegrationTest/multiModule/webinar-war/src/main/webapp/WEB-INF/web.xml
similarity index 100%
rename from subprojects/maven/src/integTest/resources/org/gradle/api/plugins/maven/MavenConversionIntegrationTest/multiModule/webinar-war/src/main/webapp/WEB-INF/web.xml
rename to subprojects/build-setup/src/integTest/resources/org/gradle/buildsetup/plugins/MavenConversionIntegrationTest/multiModule/webinar-war/src/main/webapp/WEB-INF/web.xml
diff --git a/subprojects/maven/src/integTest/resources/org/gradle/api/plugins/maven/MavenConversionIntegrationTest/multiModule/webinar-war/src/main/webapp/index.jsp b/subprojects/build-setup/src/integTest/resources/org/gradle/buildsetup/plugins/MavenConversionIntegrationTest/multiModule/webinar-war/src/main/webapp/index.jsp
similarity index 100%
rename from subprojects/maven/src/integTest/resources/org/gradle/api/plugins/maven/MavenConversionIntegrationTest/multiModule/webinar-war/src/main/webapp/index.jsp
rename to subprojects/build-setup/src/integTest/resources/org/gradle/buildsetup/plugins/MavenConversionIntegrationTest/multiModule/webinar-war/src/main/webapp/index.jsp
diff --git a/subprojects/build-setup/src/integTest/resources/org/gradle/buildsetup/plugins/MavenConversionIntegrationTest/providedNotWar/pom.xml b/subprojects/build-setup/src/integTest/resources/org/gradle/buildsetup/plugins/MavenConversionIntegrationTest/providedNotWar/pom.xml
new file mode 100644
index 0000000..911e15d
--- /dev/null
+++ b/subprojects/build-setup/src/integTest/resources/org/gradle/buildsetup/plugins/MavenConversionIntegrationTest/providedNotWar/pom.xml
@@ -0,0 +1,30 @@
+<!--
+  ~ Copyright 2013 the original author or authors.
+  ~
+  ~ Licensed under the Apache License, Version 2.0 (the "License");
+  ~ you may not use this file except in compliance with the License.
+  ~ You may obtain a copy of the License at
+  ~
+  ~      http://www.apache.org/licenses/LICENSE-2.0
+  ~
+  ~ Unless required by applicable law or agreed to in writing, software
+  ~ distributed under the License is distributed on an "AS IS" BASIS,
+  ~ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+  ~ See the License for the specific language governing permissions and
+  ~ limitations under the License.
+  -->
+
+<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
+  <modelVersion>4.0.0</modelVersion>
+  <groupId>myGroup</groupId>
+  <artifactId>myThing</artifactId>
+  <version>0.0.1-SNAPSHOT</version>
+  <dependencies>
+  	<dependency>
+  		<groupId>junit</groupId>
+  		<artifactId>junit</artifactId>
+  		<version>4.10</version>
+  		<scope>provided</scope>
+  	</dependency>
+  </dependencies>
+</project>
\ No newline at end of file
diff --git a/subprojects/maven/src/integTest/resources/org/gradle/api/plugins/maven/MavenConversionIntegrationTest/singleModule/pom.xml b/subprojects/build-setup/src/integTest/resources/org/gradle/buildsetup/plugins/MavenConversionIntegrationTest/singleModule/pom.xml
similarity index 100%
rename from subprojects/maven/src/integTest/resources/org/gradle/api/plugins/maven/MavenConversionIntegrationTest/singleModule/pom.xml
rename to subprojects/build-setup/src/integTest/resources/org/gradle/buildsetup/plugins/MavenConversionIntegrationTest/singleModule/pom.xml
diff --git a/subprojects/maven/src/integTest/resources/org/gradle/api/plugins/maven/MavenConversionIntegrationTest/singleModule/src/main/java/Foo.java b/subprojects/build-setup/src/integTest/resources/org/gradle/buildsetup/plugins/MavenConversionIntegrationTest/singleModule/src/main/java/Foo.java
similarity index 100%
rename from subprojects/maven/src/integTest/resources/org/gradle/api/plugins/maven/MavenConversionIntegrationTest/singleModule/src/main/java/Foo.java
rename to subprojects/build-setup/src/integTest/resources/org/gradle/buildsetup/plugins/MavenConversionIntegrationTest/singleModule/src/main/java/Foo.java
diff --git a/subprojects/maven/src/integTest/resources/org/gradle/api/plugins/maven/MavenConversionIntegrationTest/singleModule/src/test/java/FooTest.java b/subprojects/build-setup/src/integTest/resources/org/gradle/buildsetup/plugins/MavenConversionIntegrationTest/singleModule/src/test/java/FooTest.java
similarity index 100%
rename from subprojects/maven/src/integTest/resources/org/gradle/api/plugins/maven/MavenConversionIntegrationTest/singleModule/src/test/java/FooTest.java
rename to subprojects/build-setup/src/integTest/resources/org/gradle/buildsetup/plugins/MavenConversionIntegrationTest/singleModule/src/test/java/FooTest.java
diff --git a/subprojects/maven/src/integTest/resources/org/gradle/api/plugins/maven/MavenConversionIntegrationTest/testjar/pom.xml b/subprojects/build-setup/src/integTest/resources/org/gradle/buildsetup/plugins/MavenConversionIntegrationTest/testjar/pom.xml
similarity index 100%
rename from subprojects/maven/src/integTest/resources/org/gradle/api/plugins/maven/MavenConversionIntegrationTest/testjar/pom.xml
rename to subprojects/build-setup/src/integTest/resources/org/gradle/buildsetup/plugins/MavenConversionIntegrationTest/testjar/pom.xml
diff --git a/subprojects/maven/src/integTest/resources/org/gradle/api/plugins/maven/MavenConversionIntegrationTest/testjar/src/main/java/Foo.java b/subprojects/build-setup/src/integTest/resources/org/gradle/buildsetup/plugins/MavenConversionIntegrationTest/testjar/src/main/java/Foo.java
similarity index 100%
rename from subprojects/maven/src/integTest/resources/org/gradle/api/plugins/maven/MavenConversionIntegrationTest/testjar/src/main/java/Foo.java
rename to subprojects/build-setup/src/integTest/resources/org/gradle/buildsetup/plugins/MavenConversionIntegrationTest/testjar/src/main/java/Foo.java
diff --git a/subprojects/maven/src/integTest/resources/org/gradle/api/plugins/maven/MavenConversionIntegrationTest/testjar/src/test/java/FooTest.java b/subprojects/build-setup/src/integTest/resources/org/gradle/buildsetup/plugins/MavenConversionIntegrationTest/testjar/src/test/java/FooTest.java
similarity index 100%
rename from subprojects/maven/src/integTest/resources/org/gradle/api/plugins/maven/MavenConversionIntegrationTest/testjar/src/test/java/FooTest.java
rename to subprojects/build-setup/src/integTest/resources/org/gradle/buildsetup/plugins/MavenConversionIntegrationTest/testjar/src/test/java/FooTest.java
diff --git a/subprojects/build-setup/src/main/groovy/org/gradle/api/tasks/wrapper/Wrapper.java b/subprojects/build-setup/src/main/groovy/org/gradle/api/tasks/wrapper/Wrapper.java
new file mode 100644
index 0000000..68097ba
--- /dev/null
+++ b/subprojects/build-setup/src/main/groovy/org/gradle/api/tasks/wrapper/Wrapper.java
@@ -0,0 +1,278 @@
+/*
+ * Copyright 2010 the original author or authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.gradle.api.tasks.wrapper;
+
+import org.gradle.api.DefaultTask;
+import org.gradle.api.GradleException;
+import org.gradle.api.internal.file.FileResolver;
+import org.gradle.api.internal.plugins.StartScriptGenerator;
+import org.gradle.api.tasks.Input;
+import org.gradle.api.tasks.OutputFile;
+import org.gradle.api.tasks.TaskAction;
+import org.gradle.util.*;
+import org.gradle.wrapper.GradleWrapperMain;
+import org.gradle.wrapper.Install;
+import org.gradle.wrapper.WrapperExecutor;
+
+import java.io.File;
+import java.net.URL;
+import java.util.Properties;
+
+/**
+ * <p>Generates scripts (for *nix and windows) which allow you to build your project with Gradle, without having to
+ * install Gradle.
+ *
+ * <p>When a user executes a wrapper script the first time, the script downloads and installs the appropriate Gradle
+ * distribution and runs the build against this downloaded distribution. Any installed Gradle distribution is ignored
+ * when using the wrapper scripts.
+ *
+ * <p>The scripts generated by this task are intended to be committed to your version control system. This task also
+ * generates a small {@code gradle-wrapper.jar} bootstrap JAR file and properties file which should also be committed to
+ * your VCS. The scripts delegates to this JAR.
+ */
+public class Wrapper extends DefaultTask {
+    public static final String DEFAULT_DISTRIBUTION_PARENT_NAME = Install.DEFAULT_DISTRIBUTION_PATH;
+
+    private String distributionUrl;
+
+    /**
+     * Specifies how the wrapper path should be interpreted.
+     */
+    public enum PathBase {
+        PROJECT, GRADLE_USER_HOME
+    }
+
+    private Object scriptFile;
+    private Object jarFile;
+
+    @Input
+    private String distributionPath;
+
+    @Input
+    private PathBase distributionBase = PathBase.GRADLE_USER_HOME;
+
+    private GradleVersion gradleVersion;
+
+    @Input
+    private String archivePath;
+
+    @Input
+    private PathBase archiveBase = PathBase.GRADLE_USER_HOME;
+
+    private final DistributionLocator locator = new DistributionLocator();
+
+    public Wrapper() {
+        scriptFile = "gradlew";
+        jarFile = "gradle/wrapper/gradle-wrapper.jar";
+        distributionPath = DEFAULT_DISTRIBUTION_PARENT_NAME;
+        archivePath = DEFAULT_DISTRIBUTION_PARENT_NAME;
+        gradleVersion = GradleVersion.current();
+    }
+
+    @TaskAction
+    void generate() {
+        File jarFileDestination = getJarFile();
+        File unixScript = getScriptFile();
+        FileResolver resolver = getServices().get(FileResolver.class).withBaseDir(unixScript.getParentFile());
+        String jarFileRelativePath = resolver.resolveAsRelativePath(jarFileDestination);
+
+        writeProperties(getPropertiesFile());
+
+        URL jarFileSource = Wrapper.class.getResource("/gradle-wrapper.jar");
+        if (jarFileSource == null) {
+            throw new GradleException("Cannot locate wrapper JAR resource.");
+        }
+        GFileUtils.copyURLToFile(jarFileSource, jarFileDestination);
+
+        StartScriptGenerator generator = new StartScriptGenerator();
+        generator.setApplicationName("Gradle");
+        generator.setMainClassName(GradleWrapperMain.class.getName());
+        generator.setClasspath(WrapUtil.toList(jarFileRelativePath));
+        generator.setOptsEnvironmentVar("GRADLE_OPTS");
+        generator.setExitEnvironmentVar("GRADLE_EXIT_CONSOLE");
+        generator.setAppNameSystemProperty("org.gradle.appname");
+        generator.setScriptRelPath(unixScript.getName());
+        generator.generateUnixScript(unixScript);
+        generator.generateWindowsScript(getBatchScript());
+    }
+
+    private void writeProperties(File propertiesFileDestination) {
+        Properties wrapperProperties = new Properties();
+        wrapperProperties.put(WrapperExecutor.DISTRIBUTION_URL_PROPERTY, getDistributionUrl());
+        wrapperProperties.put(WrapperExecutor.DISTRIBUTION_BASE_PROPERTY, distributionBase.toString());
+        wrapperProperties.put(WrapperExecutor.DISTRIBUTION_PATH_PROPERTY, distributionPath);
+        wrapperProperties.put(WrapperExecutor.ZIP_STORE_BASE_PROPERTY, archiveBase.toString());
+        wrapperProperties.put(WrapperExecutor.ZIP_STORE_PATH_PROPERTY, archivePath);
+        GUtil.saveProperties(wrapperProperties, propertiesFileDestination);
+    }
+
+    /**
+     * Returns the file to write the wrapper script to.
+     */
+    @OutputFile
+    public File getScriptFile() {
+        return getProject().file(scriptFile);
+    }
+
+    public void setScriptFile(Object scriptFile) {
+        this.scriptFile = scriptFile;
+    }
+
+    /**
+     * Returns the file to write the wrapper batch script to.
+     */
+    @OutputFile
+    public File getBatchScript() {
+        File scriptFile = getScriptFile();
+        return new File(scriptFile.getParentFile(), scriptFile.getName().replaceFirst("(\\.[^\\.]+)?$", ".bat"));
+    }
+
+    /**
+     * Returns the file to write the wrapper jar file to.
+     */
+    @OutputFile
+    public File getJarFile() {
+        return getProject().file(jarFile);
+    }
+
+    public void setJarFile(Object jarFile) {
+        this.jarFile = jarFile;
+    }
+
+    /**
+     * Returns the file to write the wrapper properties to.
+     */
+    @OutputFile
+    public File getPropertiesFile() {
+        File jarFileDestination = getJarFile();
+        return new File(jarFileDestination.getParentFile(), jarFileDestination.getName().replaceAll("\\.jar$",
+                ".properties"));
+    }
+
+    /**
+     * Returns the path where the gradle distributions needed by the wrapper are unzipped. The path is relative to the
+     * distribution base directory
+     *
+     * @see #setDistributionPath(String)
+     */
+    public String getDistributionPath() {
+        return distributionPath;
+    }
+
+    /**
+     * Sets the path where the gradle distributions needed by the wrapper are unzipped. The path is relative to the
+     * distribution base directory
+     *
+     * @see #setDistributionPath(String)
+     */
+    public void setDistributionPath(String distributionPath) {
+        this.distributionPath = distributionPath;
+    }
+
+    /**
+     * Returns the gradle version for the wrapper.
+     *
+     * @see #setGradleVersion(String)
+     */
+    public String getGradleVersion() {
+        return gradleVersion.getVersion();
+    }
+
+    /**
+     * The version of the gradle distribution required by the wrapper. This is usually the same version of Gradle you
+     * use for building your project.
+     */
+    public void setGradleVersion(String gradleVersion) {
+        this.gradleVersion = GradleVersion.version(gradleVersion);
+    }
+
+    /**
+     * The URL to download the gradle distribution from.
+     *
+     * <p>If not set, the download URL is the default for the specified {@link #getGradleVersion()}.
+     *
+     * <p>If {@link #getGradleVersion()} is not set, will return null.
+     *
+     * <p>The wrapper downloads a certain distribution only once and caches it. If your distribution base is the
+     * project, you might submit the distribution to your version control system. That way no download is necessary at
+     * all. This might be in particular interesting, if you provide a custom gradle snapshot to the wrapper, because you
+     * don't need to provide a download server then.
+     */
+    @Input
+    public String getDistributionUrl() {
+        if (distributionUrl != null) {
+            return distributionUrl;
+        } else if (gradleVersion != null) {
+            return locator.getDistributionFor(gradleVersion).toString();
+        } else {
+            return null;
+        }
+    }
+
+    public void setDistributionUrl(String url) {
+        this.distributionUrl = url;
+    }
+
+    /**
+     * The distribution base specifies whether the unpacked wrapper distribution should be stored in the project or in
+     * the gradle user home dir.
+     */
+    public PathBase getDistributionBase() {
+        return distributionBase;
+    }
+
+    /**
+     * The distribution base specifies whether the unpacked wrapper distribution should be stored in the project or in
+     * the gradle user home dir.
+     */
+    public void setDistributionBase(PathBase distributionBase) {
+        this.distributionBase = distributionBase;
+    }
+
+    /**
+     * Returns the path where the gradle distributions archive should be saved (i.e. the parent dir). The path is
+     * relative to the archive base directory.
+     */
+    public String getArchivePath() {
+        return archivePath;
+    }
+
+    /**
+     * Set's the path where the gradle distributions archive should be saved (i.e. the parent dir). The path is relative
+     * to the parent dir specified with {@link #getArchiveBase()}.
+     */
+    public void setArchivePath(String archivePath) {
+        this.archivePath = archivePath;
+    }
+
+    /**
+     * The archive base specifies whether the unpacked wrapper distribution should be stored in the project or in the
+     * gradle user home dir.
+     */
+    public PathBase getArchiveBase() {
+        return archiveBase;
+    }
+
+    /**
+     * The archive base specifies whether the unpacked wrapper distribution should be stored in the project or in the
+     * gradle user home dir.
+     */
+    public void setArchiveBase(PathBase archiveBase) {
+        this.archiveBase = archiveBase;
+    }
+
+}
diff --git a/subprojects/plugins/src/main/groovy/org/gradle/api/tasks/wrapper/package-info.java b/subprojects/build-setup/src/main/groovy/org/gradle/api/tasks/wrapper/package-info.java
similarity index 100%
rename from subprojects/plugins/src/main/groovy/org/gradle/api/tasks/wrapper/package-info.java
rename to subprojects/build-setup/src/main/groovy/org/gradle/api/tasks/wrapper/package-info.java
diff --git a/subprojects/build-setup/src/main/groovy/org/gradle/buildsetup/plugins/BuildSetupPlugin.groovy b/subprojects/build-setup/src/main/groovy/org/gradle/buildsetup/plugins/BuildSetupPlugin.groovy
new file mode 100644
index 0000000..283fe7d
--- /dev/null
+++ b/subprojects/build-setup/src/main/groovy/org/gradle/buildsetup/plugins/BuildSetupPlugin.groovy
@@ -0,0 +1,87 @@
+/*
+ * Copyright 2012 the original author or authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.gradle.buildsetup.plugins
+
+import org.gradle.api.Incubating
+import org.gradle.api.Plugin
+import org.gradle.api.Project
+import org.gradle.api.Task
+import org.gradle.api.internal.DocumentationRegistry
+import org.gradle.api.internal.artifacts.DependencyManagementServices
+import org.gradle.api.internal.artifacts.mvnsettings.MavenSettingsProvider
+import org.gradle.api.internal.file.FileResolver
+import org.gradle.buildsetup.plugins.internal.ProjectLayoutSetupRegistry
+import org.gradle.buildsetup.plugins.internal.ProjectLayoutSetupRegistryFactory
+import org.gradle.buildsetup.tasks.SetupBuild
+
+import javax.inject.Inject
+
+ at Incubating
+class BuildSetupPlugin implements Plugin<Project> {
+    public static final String SETUP_BUILD_TASK_NAME = "setupBuild"
+    public static final String GROUP = 'Build Setup'
+
+    private final MavenSettingsProvider mavenSettingsProvider
+    private final DocumentationRegistry documentationRegistry
+    private final FileResolver fileResolver
+
+    @Inject
+    BuildSetupPlugin(DependencyManagementServices dependencyManagementServices, DocumentationRegistry documentationRegistry, FileResolver fileResolver) {
+        this.fileResolver = fileResolver
+        this.documentationRegistry = documentationRegistry
+        this.mavenSettingsProvider = dependencyManagementServices.get(MavenSettingsProvider)
+    }
+
+    void apply(Project project) {
+        ProjectLayoutSetupRegistryFactory projectLayoutRegistryFactory = new ProjectLayoutSetupRegistryFactory(mavenSettingsProvider,
+                documentationRegistry,
+                fileResolver);
+
+        Task setupBuild = project.getTasks().create(SETUP_BUILD_TASK_NAME, SetupBuild);
+        ProjectLayoutSetupRegistry projectLayoutRegistry = projectLayoutRegistryFactory.createProjectLayoutSetupRegistry()
+        setupBuild.projectLayoutRegistry = projectLayoutRegistry
+        setupBuild.group = GROUP
+        setupBuild.description = "Initializes a new Gradle build. [incubating]"
+        Closure setupCanBeSkipped = {
+            if (project.file("build.gradle").exists()) {
+                return ("The build file 'build.gradle' already exists. Skipping build initialization.")
+            }
+            if (project.buildFile?.exists()) {
+                return ("The build file '$project.buildFile.name' already exists. Skipping build initialization.")
+            }
+            if (project.file("settings.gradle").exists()) {
+                return ("The settings file 'settings.gradle' already exists. Skipping build initialization.")
+            }
+            if (project.subprojects.size() > 0) {
+                return ("This Gradle project appears to be part of an existing multi-project Gradle build. Skipping build initialization.")
+            }
+            return null
+        }
+        setupBuild.onlyIf {
+            def skippedMsg = setupCanBeSkipped()
+            if (skippedMsg) {
+                project.logger.warn skippedMsg
+                return false
+            }
+            return true
+        }
+
+        if (!setupCanBeSkipped()) {
+            setupBuild.dependsOn("wrapper")
+        }
+    }
+}
\ No newline at end of file
diff --git a/subprojects/build-setup/src/main/groovy/org/gradle/buildsetup/plugins/WrapperPlugin.groovy b/subprojects/build-setup/src/main/groovy/org/gradle/buildsetup/plugins/WrapperPlugin.groovy
new file mode 100644
index 0000000..682890a
--- /dev/null
+++ b/subprojects/build-setup/src/main/groovy/org/gradle/buildsetup/plugins/WrapperPlugin.groovy
@@ -0,0 +1,34 @@
+/*
+ * Copyright 2013 the original author or authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+
+
+package org.gradle.buildsetup.plugins
+
+import org.gradle.api.Incubating
+import org.gradle.api.Plugin
+import org.gradle.api.Project
+import org.gradle.api.Task
+import org.gradle.api.tasks.wrapper.Wrapper
+
+ at Incubating
+class WrapperPlugin implements Plugin<Project> {
+    void apply(Project project) {
+        Task wrapper = project.tasks.create("wrapper", Wrapper)
+        wrapper.group = BuildSetupPlugin.GROUP
+        wrapper.description = "Generates Gradle wrapper files. [incubating]"
+    }
+}
diff --git a/subprojects/build-setup/src/main/groovy/org/gradle/buildsetup/plugins/internal/BasicProjectSetupDescriptor.groovy b/subprojects/build-setup/src/main/groovy/org/gradle/buildsetup/plugins/internal/BasicProjectSetupDescriptor.groovy
new file mode 100644
index 0000000..62178b5
--- /dev/null
+++ b/subprojects/build-setup/src/main/groovy/org/gradle/buildsetup/plugins/internal/BasicProjectSetupDescriptor.groovy
@@ -0,0 +1,41 @@
+/*
+ * Copyright 2013 the original author or authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.gradle.buildsetup.plugins.internal
+
+import org.gradle.api.internal.DocumentationRegistry
+import org.gradle.api.internal.file.FileResolver
+
+class BasicProjectSetupDescriptor extends TemplateBasedProjectSetupDescriptor {
+
+    public BasicProjectSetupDescriptor(FileResolver fileResolver, DocumentationRegistry documentationRegistry) {
+        super(fileResolver, documentationRegistry)
+    }
+
+    String getId() {
+        BuildSetupTypeIds.BASIC
+    }
+
+    @Override
+    URL getBuildFileTemplate() {
+        return BasicProjectSetupDescriptor.class.getResource("/org/gradle/buildsetup/tasks/templates/build.gradle.template");
+    }
+
+    @Override
+    URL getSettingsTemplate() {
+        return BasicProjectSetupDescriptor.class.getResource("/org/gradle/buildsetup/tasks/templates/settings.gradle.template")
+    }
+}
diff --git a/subprojects/build-setup/src/main/groovy/org/gradle/buildsetup/plugins/internal/BuildSetupAutoApplyAction.java b/subprojects/build-setup/src/main/groovy/org/gradle/buildsetup/plugins/internal/BuildSetupAutoApplyAction.java
new file mode 100644
index 0000000..0fa2b0d
--- /dev/null
+++ b/subprojects/build-setup/src/main/groovy/org/gradle/buildsetup/plugins/internal/BuildSetupAutoApplyAction.java
@@ -0,0 +1,33 @@
+/*
+ * Copyright 2013 the original author or authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.gradle.buildsetup.plugins.internal;
+
+import org.gradle.api.internal.project.ProjectInternal;
+import org.gradle.configuration.project.ProjectConfigureAction;
+
+public class BuildSetupAutoApplyAction implements ProjectConfigureAction {
+
+    public void execute(final ProjectInternal projectInternal) {
+        if (projectInternal.getParent() == null) {
+            projectInternal.getTasks().addPlaceholderAction("setupBuild", new Runnable() {
+                public void run() {
+                    projectInternal.getPlugins().apply("build-setup");
+                }
+            });
+        }
+    }
+}
diff --git a/subprojects/build-setup/src/main/groovy/org/gradle/buildsetup/plugins/internal/BuildSetupTypeIds.java b/subprojects/build-setup/src/main/groovy/org/gradle/buildsetup/plugins/internal/BuildSetupTypeIds.java
new file mode 100644
index 0000000..8f3bd58
--- /dev/null
+++ b/subprojects/build-setup/src/main/groovy/org/gradle/buildsetup/plugins/internal/BuildSetupTypeIds.java
@@ -0,0 +1,27 @@
+/*
+ * Copyright 2013 the original author or authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.gradle.buildsetup.plugins.internal;
+
+public abstract class BuildSetupTypeIds {
+
+    private BuildSetupTypeIds() {}
+
+    public static final String BASIC = "basic";
+    public static final String POM = "pom";
+    public static final String JAVA_LIBRARY = "java-library";
+
+}
diff --git a/subprojects/build-setup/src/main/groovy/org/gradle/buildsetup/plugins/internal/JavaLibraryProjectSetupDescriptor.groovy b/subprojects/build-setup/src/main/groovy/org/gradle/buildsetup/plugins/internal/JavaLibraryProjectSetupDescriptor.groovy
new file mode 100644
index 0000000..01e9be7
--- /dev/null
+++ b/subprojects/build-setup/src/main/groovy/org/gradle/buildsetup/plugins/internal/JavaLibraryProjectSetupDescriptor.groovy
@@ -0,0 +1,57 @@
+/*
+ * Copyright 2012 the original author or authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.gradle.buildsetup.plugins.internal
+
+import org.gradle.api.internal.DocumentationRegistry
+import org.gradle.api.internal.file.FileResolver
+
+class JavaLibraryProjectSetupDescriptor extends TemplateBasedProjectSetupDescriptor {
+
+    private final FileResolver fileResolver
+
+    public JavaLibraryProjectSetupDescriptor(FileResolver fileResolver, DocumentationRegistry documentationRegistry) {
+        super(fileResolver, documentationRegistry);
+        this.fileResolver = fileResolver;
+    }
+
+    String getId() {
+        BuildSetupTypeIds.JAVA_LIBRARY
+    }
+
+    URL getBuildFileTemplate() {
+        return JavaLibraryProjectSetupDescriptor.class.getResource("/org/gradle/buildsetup/tasks/templates/java-library-build.gradle.template");
+    }
+
+    URL getSettingsTemplate() {
+        return JavaLibraryProjectSetupDescriptor.class.getResource("/org/gradle/buildsetup/tasks/templates/settings.gradle.template")
+    }
+
+    void generateProjectSources() {
+        if (fileResolver.resolveFilesAsTree("src/main/java").empty || fileResolver.resolveFilesAsTree("src/test/java").empty) {
+            generateClass("src/main/java", "Library.java")
+            generateClass("src/test/java", "LibraryTest.java")
+        }
+    }
+
+    def generateClass(String sourceRoot, String clazzFileName) {
+        File sourceRootFolder = fileResolver.resolve(sourceRoot)
+        sourceRootFolder.mkdirs()
+        File clazzFile = new File(sourceRootFolder, clazzFileName)
+        URL productionClazzFileTemplate = JavaLibraryProjectSetupDescriptor.class.getResource("/org/gradle/buildsetup/tasks/templates/${clazzFileName}.template");
+        generateFileFromTemplate(productionClazzFileTemplate, clazzFile, [:])
+    }
+}
\ No newline at end of file
diff --git a/subprojects/build-setup/src/main/groovy/org/gradle/buildsetup/plugins/internal/PomProjectSetupDescriptor.groovy b/subprojects/build-setup/src/main/groovy/org/gradle/buildsetup/plugins/internal/PomProjectSetupDescriptor.groovy
new file mode 100644
index 0000000..0c4302a
--- /dev/null
+++ b/subprojects/build-setup/src/main/groovy/org/gradle/buildsetup/plugins/internal/PomProjectSetupDescriptor.groovy
@@ -0,0 +1,51 @@
+/*
+ * Copyright 2012 the original author or authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.gradle.buildsetup.plugins.internal
+
+import org.gradle.api.internal.artifacts.mvnsettings.MavenSettingsProvider
+import org.gradle.api.internal.file.FileResolver
+import org.gradle.buildsetup.plugins.internal.maven.Maven2Gradle
+import org.gradle.buildsetup.plugins.internal.maven.MavenConversionException
+import org.gradle.buildsetup.plugins.internal.maven.MavenProjectsCreator
+import org.gradle.util.SingleMessageLogger
+
+class PomProjectSetupDescriptor implements ProjectSetupDescriptor {
+    private final MavenSettingsProvider settingsProvider
+    private final FileResolver fileResolver
+
+
+    PomProjectSetupDescriptor(FileResolver fileResolver, MavenSettingsProvider mavenSettingsProvider) {
+        this.fileResolver = fileResolver
+        this.settingsProvider = mavenSettingsProvider
+    }
+
+    String getId() {
+        BuildSetupTypeIds.POM
+    }
+
+    void generateProject() {
+        SingleMessageLogger.incubatingFeatureUsed("Maven to Gradle conversion")
+        def pom = fileResolver.resolve("pom.xml")
+        try {
+            def settings = settingsProvider.buildSettings()
+            def mavenProjects = new MavenProjectsCreator().create(settings, pom)
+            new Maven2Gradle(mavenProjects).convert()
+        } catch (Exception exception) {
+            throw new MavenConversionException("Could not convert Maven POM $pom to a Gradle build.", exception)
+        }
+    }
+}
diff --git a/subprojects/build-setup/src/main/groovy/org/gradle/buildsetup/plugins/internal/ProjectLayoutSetupRegistry.groovy b/subprojects/build-setup/src/main/groovy/org/gradle/buildsetup/plugins/internal/ProjectLayoutSetupRegistry.groovy
new file mode 100644
index 0000000..dacc465
--- /dev/null
+++ b/subprojects/build-setup/src/main/groovy/org/gradle/buildsetup/plugins/internal/ProjectLayoutSetupRegistry.groovy
@@ -0,0 +1,47 @@
+/*
+ * Copyright 2012 the original author or authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.gradle.buildsetup.plugins.internal
+
+import org.gradle.api.GradleException
+import org.gradle.api.logging.Logger
+import org.gradle.api.logging.Logging
+
+class ProjectLayoutSetupRegistry {
+
+    private final Logger logger = Logging.getLogger(ProjectLayoutSetupRegistry.class);
+    private final Map<String, ProjectSetupDescriptor> registeredProjectDescriptors = new HashMap<String, ProjectSetupDescriptor>();
+
+    void add(ProjectSetupDescriptor descriptor) {
+        if (registeredProjectDescriptors.containsKey(descriptor.id)) {
+            throw new GradleException("ProjectDescriptor with ID '${descriptor.id}' already registered.")
+        }
+        registeredProjectDescriptors.put(descriptor.id, descriptor)
+        logger.debug("registered setupDescriptor {}", descriptor.id)
+    }
+
+    ProjectSetupDescriptor get(String type) {
+        return registeredProjectDescriptors.get(type)
+    }
+
+    List<ProjectSetupDescriptor> getAll() {
+        return Arrays.asList(registeredProjectDescriptors.values())
+    }
+
+    boolean supports(String type) {
+        return get(type) != null
+    }
+}
diff --git a/subprojects/build-setup/src/main/groovy/org/gradle/buildsetup/plugins/internal/ProjectLayoutSetupRegistryFactory.groovy b/subprojects/build-setup/src/main/groovy/org/gradle/buildsetup/plugins/internal/ProjectLayoutSetupRegistryFactory.groovy
new file mode 100644
index 0000000..6647bdb
--- /dev/null
+++ b/subprojects/build-setup/src/main/groovy/org/gradle/buildsetup/plugins/internal/ProjectLayoutSetupRegistryFactory.groovy
@@ -0,0 +1,47 @@
+/*
+ * Copyright 2012 the original author or authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.gradle.buildsetup.plugins.internal
+
+import org.gradle.api.internal.DocumentationRegistry
+import org.gradle.api.internal.artifacts.mvnsettings.MavenSettingsProvider
+import org.gradle.api.internal.file.FileResolver
+
+class ProjectLayoutSetupRegistryFactory {
+    private final DocumentationRegistry documentationRegistry
+    private final MavenSettingsProvider mavenSettingsProvider
+    private final FileResolver fileResolver
+
+    public ProjectLayoutSetupRegistryFactory(MavenSettingsProvider mavenSettingsProvider,
+                                             DocumentationRegistry documentationRegistry,
+                                             FileResolver fileResolver) {
+        this.mavenSettingsProvider = mavenSettingsProvider
+        this.documentationRegistry = documentationRegistry
+        this.fileResolver = fileResolver
+    }
+
+    ProjectLayoutSetupRegistry createProjectLayoutSetupRegistry() {
+        ProjectLayoutSetupRegistry registry = new ProjectLayoutSetupRegistry()
+
+        // TODO maybe referencing the implementation class here is enough and instantiation
+        // should be defererred when descriptor is requested.
+        registry.add(new BasicProjectSetupDescriptor(fileResolver, documentationRegistry));
+        registry.add(new JavaLibraryProjectSetupDescriptor(fileResolver, documentationRegistry));
+        registry.add(new PomProjectSetupDescriptor(fileResolver, mavenSettingsProvider))
+        return registry
+    }
+
+}
diff --git a/subprojects/build-setup/src/main/groovy/org/gradle/buildsetup/plugins/internal/ProjectSetupDescriptor.groovy b/subprojects/build-setup/src/main/groovy/org/gradle/buildsetup/plugins/internal/ProjectSetupDescriptor.groovy
new file mode 100644
index 0000000..9edb334
--- /dev/null
+++ b/subprojects/build-setup/src/main/groovy/org/gradle/buildsetup/plugins/internal/ProjectSetupDescriptor.groovy
@@ -0,0 +1,25 @@
+/*
+ * Copyright 2012 the original author or authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.gradle.buildsetup.plugins.internal
+
+interface ProjectSetupDescriptor {
+
+    String getId()
+
+    void generateProject()
+
+}
diff --git a/subprojects/build-setup/src/main/groovy/org/gradle/buildsetup/plugins/internal/TemplateBasedProjectSetupDescriptor.groovy b/subprojects/build-setup/src/main/groovy/org/gradle/buildsetup/plugins/internal/TemplateBasedProjectSetupDescriptor.groovy
new file mode 100644
index 0000000..e5e35cb
--- /dev/null
+++ b/subprojects/build-setup/src/main/groovy/org/gradle/buildsetup/plugins/internal/TemplateBasedProjectSetupDescriptor.groovy
@@ -0,0 +1,73 @@
+/*
+ * Copyright 2012 the original author or authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.gradle.buildsetup.plugins.internal
+
+import groovy.text.SimpleTemplateEngine
+import groovy.text.Template
+import org.gradle.api.internal.DocumentationRegistry
+import org.gradle.api.internal.file.FileResolver
+import org.gradle.util.GradleVersion
+
+import java.text.DateFormat
+
+abstract class TemplateBasedProjectSetupDescriptor implements ProjectSetupDescriptor {
+
+    private final DocumentationRegistry documentationRegistry
+    private final FileResolver fileResolver
+
+    TemplateBasedProjectSetupDescriptor(FileResolver fileResolver, DocumentationRegistry documentationRegistry) {
+        this.fileResolver = fileResolver
+        this.documentationRegistry = documentationRegistry
+    }
+
+    abstract URL getBuildFileTemplate()
+
+    abstract URL getSettingsTemplate()
+
+    void generateProject() {
+        generateGradleFiles()
+        generateProjectSources()
+    }
+
+    void generateProjectSources() {
+    }
+
+    def generateGradleFiles() {
+        generateFileFromTemplate(getBuildFileTemplate(), fileResolver.resolve("build.gradle"), getAdditionalBuildFileTemplateBindings())
+        generateFileFromTemplate(getSettingsTemplate(), fileResolver.resolve("settings.gradle"), getAdditionalSettingsFileTemplateBindings())
+    }
+
+    protected Map getAdditionalBuildFileTemplateBindings() {
+        return [ref_userguide_java_tutorial: documentationRegistry.getDocumentationFor("tutorial_java_projects")]
+    }
+
+    protected Map getAdditionalSettingsFileTemplateBindings() {
+        return [ref_userguide_multiproject: documentationRegistry.getDocumentationFor("multi_project_builds"), rootProjectName: fileResolver.resolve(".").name]
+    }
+
+    protected generateFileFromTemplate(URL templateURL, File targetFile, Map additionalBindings) {
+        SimpleTemplateEngine templateEngine = new SimpleTemplateEngine()
+        String now = DateFormat.getDateTimeInstance(DateFormat.SHORT, DateFormat.SHORT).format(new Date())
+        def bindings = [genDate: now, genUser: System.getProperty("user.name"), genGradleVersion: GradleVersion.current().toString()]
+        bindings += additionalBindings
+        Template template = templateEngine.createTemplate(templateURL.text)
+        Map wrappedBindings = bindings.collectEntries { key, value -> [key, new TemplateValue(value.toString())] }
+        targetFile.withWriter("utf-8") { writer ->
+            template.make(wrappedBindings).writeTo(writer)
+        }
+    }
+}
diff --git a/subprojects/build-setup/src/main/groovy/org/gradle/buildsetup/plugins/internal/TemplateValue.java b/subprojects/build-setup/src/main/groovy/org/gradle/buildsetup/plugins/internal/TemplateValue.java
new file mode 100644
index 0000000..9e05f53
--- /dev/null
+++ b/subprojects/build-setup/src/main/groovy/org/gradle/buildsetup/plugins/internal/TemplateValue.java
@@ -0,0 +1,67 @@
+/*
+ * Copyright 2013 the original author or authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.gradle.buildsetup.plugins.internal;
+
+public class TemplateValue {
+    private final String value;
+
+    public TemplateValue(String value) {
+        this.value = value;
+    }
+
+    public String getGroovyComment() {
+        return value.replace("\\", "\\\\");
+    }
+
+    public String getGroovyString() {
+        StringBuilder result = new StringBuilder();
+        for (int i = 0; i < value.length(); i++) {
+            char ch = value.charAt(i);
+            switch (ch) {
+                case '\\':
+                    result.append('\\').append('\\');
+                    break;
+                case '\'':
+                    result.append('\\').append('\'');
+                    break;
+                case '\n':
+                    result.append('\\').append('n');
+                    break;
+                case '\r':
+                    result.append('\\').append('r');
+                    break;
+                case '\t':
+                    result.append('\\').append('t');
+                    break;
+                case '\f':
+                    result.append('\\').append('f');
+                    break;
+                case '\b':
+                    result.append('\\').append('b');
+                    break;
+                default:
+                    result.append(ch);
+            }
+        }
+        return result.toString();
+    }
+
+    @Override
+    public String toString() {
+        return String.format(">>>%s<<<", value);
+    }
+}
diff --git a/subprojects/build-setup/src/main/groovy/org/gradle/buildsetup/plugins/internal/WrapperPluginAutoApplyAction.groovy b/subprojects/build-setup/src/main/groovy/org/gradle/buildsetup/plugins/internal/WrapperPluginAutoApplyAction.groovy
new file mode 100644
index 0000000..570e3d4
--- /dev/null
+++ b/subprojects/build-setup/src/main/groovy/org/gradle/buildsetup/plugins/internal/WrapperPluginAutoApplyAction.groovy
@@ -0,0 +1,32 @@
+/*
+ * Copyright 2013 the original author or authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.gradle.buildsetup.plugins.internal
+
+import org.gradle.api.internal.project.ProjectInternal
+import org.gradle.configuration.project.ProjectConfigureAction
+
+class WrapperPluginAutoApplyAction implements ProjectConfigureAction {
+    void execute(ProjectInternal projectInternal) {
+        if (projectInternal.getParent() == null) {
+            projectInternal.tasks.addPlaceholderAction("wrapper", new Runnable() {
+                void run() {
+                    projectInternal.getPlugins().apply("wrapper")
+                }
+            })
+        }
+    }
+}
diff --git a/subprojects/build-setup/src/main/groovy/org/gradle/buildsetup/plugins/internal/maven/Maven2Gradle.groovy b/subprojects/build-setup/src/main/groovy/org/gradle/buildsetup/plugins/internal/maven/Maven2Gradle.groovy
new file mode 100644
index 0000000..55a108c
--- /dev/null
+++ b/subprojects/build-setup/src/main/groovy/org/gradle/buildsetup/plugins/internal/maven/Maven2Gradle.groovy
@@ -0,0 +1,514 @@
+/*
+ * Copyright 2012 the original author or authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+
+
+package org.gradle.buildsetup.plugins.internal.maven
+
+import org.gradle.mvn3.org.apache.maven.project.MavenProject
+import org.gradle.util.GFileUtils
+
+/**
+ * This script obtains the effective POM of the current project, reads its dependencies
+ * and generates build.gradle scripts. It also generates settings.gradle for multimodule builds. <br/>
+ *
+ * It currently supports both single-module and multi-module POMs, inheritance, dependency management, properties - everything.
+ */
+class Maven2Gradle {
+
+    def dependentWars = []
+    def qualifiedNames
+    def workingDir
+    def effectivePom
+
+    private Set<MavenProject> mavenProjects
+
+    Maven2Gradle(Set<MavenProject> mavenProjects) {
+        assert !mavenProjects.empty: "No Maven projects provided."
+        this.mavenProjects = mavenProjects
+    }
+
+    def convert() {
+        workingDir = new File('.').canonicalFile
+
+        //For now we're building the effective POM XML from the model
+        //and then we parse the XML using slurper.
+        //This way we don't have to rewrite the Maven2Gradle just yet.
+        //Maven2Gradle should be rewritten (with coverage) so that feeds of the maven object model, not XML.
+        def effectivePom = new MavenProjectXmlWriter().toXml(mavenProjects)
+        //use the Groovy XmlSlurper library to parse the text string
+        this.effectivePom = new XmlSlurper().parseText(effectivePom)
+
+        String build
+        def multimodule = this.effectivePom.name() == "projects"
+
+        if (multimodule) {
+            def allProjects = this.effectivePom.project
+            qualifiedNames = generateSettings(workingDir.getName(), allProjects[0].artifactId, allProjects);
+            def dependencies = [:];
+            allProjects.each { project ->
+                dependencies[project.artifactId.text()] = getDependencies(project, allProjects)
+            }
+
+            def commonDeps = dependencies.get(allProjects[0].artifactId.text())
+            build = """allprojects  {
+  apply plugin: 'maven'
+
+  ${getArtifactData(allProjects[0])}
+}
+
+subprojects {
+  apply plugin: 'java'
+  ${compilerSettings(allProjects[0], "  ")}
+  ${packageSources(allProjects[0])}
+  ${getRepositoriesForProjects(allProjects)}
+  ${globalExclusions(allProjects[0])}
+  ${commonDeps}
+  ${testNg(commonDeps)}
+}
+"""
+            modules(allProjects, false).each { module ->
+                def id = module.artifactId.text()
+                String moduleDependencies = dependencies.get(id)
+                boolean warPack = module.packaging.text().equals("war")
+                def hasDependencies = !(moduleDependencies == null || moduleDependencies.length() == 0)
+                File submoduleBuildFile = new File(projectDir(module), 'build.gradle')
+                def group = ''
+                if (module.groupId != allProjects[0].groupId) {
+                    group = "group = '${module.groupId}'"
+                }
+                String moduleBuild = """
+${group}
+description = '${module.name}'
+
+"""
+                if (warPack) {
+                    moduleBuild += """apply plugin: 'war'
+
+"""
+                    if (dependentWars.any { project ->
+                        project.groupId.text() == module.groupId.text() &&
+                                project.artifactId.text() == id
+                    }) {
+                        moduleBuild += """jar.enabled = true
+"""
+                    }
+                }
+                if (hasDependencies) {
+                    moduleBuild += moduleDependencies
+                }
+
+                moduleBuild += testNg(moduleDependencies)
+
+                if (submoduleBuildFile.exists()) {
+                    submoduleBuildFile.renameTo(new File("build.gradle.bak"))
+                }
+                def packageTests = packageTests(module);
+                if (packageTests) {
+                    moduleBuild += packageTests;
+                }
+                submoduleBuildFile.text = moduleBuild
+            }
+            //TODO deployment
+        } else {//simple
+            build = """apply plugin: 'java'
+apply plugin: 'maven'
+
+${getArtifactData(this.effectivePom)}
+
+description = \"""${this.effectivePom.name}\"""
+
+${compilerSettings(this.effectivePom, "")}
+${globalExclusions(this.effectivePom)}
+
+"""
+
+            Set<String> repoSet = new LinkedHashSet<String>();
+            getRepositoriesForModule(this.effectivePom, repoSet)
+            String repos = """repositories {
+        $localRepoUri
+"""
+            repoSet.each {
+                repos = "${repos} ${it}\n"
+            }
+            build += "${repos}}\n"
+            String dependencies = getDependencies(this.effectivePom, null)
+            build += dependencies
+
+            String packageTests = packageTests(this.effectivePom);
+            if (packageTests) {
+                build += '//packaging tests'
+                build += packageTests;
+            }
+            generateSettings(workingDir.getName(), this.effectivePom.artifactId, null);
+        }
+        def buildFile = new File("build.gradle")
+        if (buildFile.exists()) {
+            buildFile.renameTo(new File("build.gradle.bak"))
+        }
+        buildFile.text = build
+    }
+
+    def globalExclusions = { project ->
+        def exclusions = ''
+        def enforcerPlugin = plugin('maven-enforcer-plugin', project)
+        def enforceGoal = pluginGoal('enforce', enforcerPlugin)
+        if (enforceGoal) {
+            exclusions += 'configurations.all {\n'
+            enforceGoal.configuration.rules.bannedDependencies.excludes.childNodes().each {
+                def tokens = it.text().tokenize(':')
+                exclusions += "it.exclude group: '${tokens[0]}'"
+                if (tokens.size() > 1 && tokens[1] != '*') {
+                    exclusions += ", module: '${tokens[1]}'"
+                }
+                exclusions += '\n'
+            }
+        }
+        exclusions = exclusions ? exclusions += '}' : exclusions
+        exclusions
+    }
+
+    def testNg = { moduleDependencies ->
+        if (moduleDependencies.contains('testng')) {
+            """test.useTestNG()
+"""
+        } else {
+            ''
+        }
+    }
+
+    def modules = { allProjects, incReactors ->
+        return allProjects.findAll { project ->
+            project.parent.text().length() > 0 && (incReactors || project.packaging.text() != 'pom')
+        }
+    }
+
+    def fqn = { project, allProjects ->
+        def buffer = new StringBuilder()
+        generateFqn(project, allProjects, buffer)
+        return buffer.toString()
+    }
+
+    private generateFqn(def project, def allProjects, StringBuilder buffer) {
+        def artifactId = project.artifactId.text()
+        buffer.insert(0, ":${artifactId}")
+        //we don't need the top-level parent in gradle, so we stop on it
+        if (project.parent.artifactId.text() != allProjects[0].artifactId.text()) {
+            generateFqn(allProjects.find { fullProject ->
+                fullProject.artifactId.text() == project.parent.artifactId.text()
+            }, allProjects, buffer)
+        }
+    }
+
+
+    def localRepoUri = {
+        """mavenLocal()
+    """
+    }
+
+    private String getArtifactData(project) {
+        return """group = '$project.groupId'
+version = '$project.version'""";
+    }
+
+    private String getRepositoriesForProjects(projects) {
+        String repos = """repositories {
+    ${localRepoUri()}
+"""
+        def repoSet = new LinkedHashSet<String>();
+        projects.each {
+            getRepositoriesForModule(it, repoSet)
+        }
+        repoSet.each {
+            repos = "${repos}${it}\n"
+        }
+        repos = "${repos}  }\n"
+        return repos
+    }
+
+    private void getRepositoriesForModule(module, repoSet) {
+        module.repositories.repository.each {
+            repoSet.add("    maven { url \"${it.url}\" }")
+        }
+        //No need to include plugin repos - who cares about maven plugins?
+    }
+
+    private String getDependencies(project, allProjects) {
+        // use GPath to navigate the object hierarchy and retrieve the collection of dependency nodes.
+        def dependencies = project.dependencies.dependency
+        def war = project.packaging == "war"
+
+        def compileTimeScope = []
+        def runTimeScope = []
+        def testScope = []
+        def providedScope = []
+        def systemScope = []
+
+        //cleanup duplicates from parent
+
+// using Groovy Looping and mapping a Groovy Closure to each element, we collect together all
+// the dependency nodes into corresponding collections depending on their scope value.
+        dependencies.each() {
+            if (!duplicateDependency(it, project, allProjects)) {
+                def scope = (elementHasText(it.scope)) ? it.scope : "compile"
+                switch (scope) {
+                    case "compile":
+                        compileTimeScope.add(it)
+                        break
+                    case "test":
+                        testScope.add(it)
+                        break
+                    case "provided":
+                        providedScope.add(it)
+                        break
+                    case "runtime":
+                        runTimeScope.add(it)
+                        break
+                    case "system":
+                        systemScope.add(it)
+                        break
+                }
+            }
+        }
+
+        /**
+         * print function then checks the exclusions node to see if it exists, if
+         * so it branches off, otherwise we call our simple print function
+         */
+        def createGradleDep = { String scope, StringBuilder sb, mavenDependency ->
+            def projectDep = allProjects.find { prj ->
+                return prj.artifactId.text() == mavenDependency.artifactId.text() && prj.groupId.text() == mavenDependency.groupId.text()
+            }
+
+            if (projectDep) {
+                createProjectDependency(projectDep, sb, scope, allProjects)
+            } else {
+                def providedMessage = "";
+                if (!war && scope == 'providedCompile') {
+                    scope = 'compile'
+                    providedMessage = '''\
+                       /* This dependency was originally in the Maven provided scope, but the project was not of type war.
+                       This behavior is not yet supported by Gradle, so this dependency has been converted to a compile dependency.
+                       Please review and delete this closure when resolved. */
+                       '''.stripIndent(16)
+                }
+                def exclusions = mavenDependency.exclusions.exclusion
+                if (exclusions.size() > 0 || providedMessage != "") {
+                    createComplexDependency(mavenDependency, sb, scope, providedMessage)
+                } else {
+                    createBasicDependency(mavenDependency, sb, scope)
+                }
+            }
+        }
+
+
+        StringBuilder build = new StringBuilder()
+        if (!compileTimeScope.isEmpty() || !runTimeScope.isEmpty() || !testScope.isEmpty() || !providedScope.isEmpty() || !systemScope.isEmpty()) {
+            build.append("dependencies {").append("\n")
+// for each collection, one at a time, we take each element and call our print function
+            if (!compileTimeScope.isEmpty()) {
+                compileTimeScope.each() { createGradleDep("compile", build, it) }
+            }
+            if (!runTimeScope.isEmpty()) {
+                runTimeScope.each() { createGradleDep("runtime", build, it) }
+            }
+            if (!testScope.isEmpty()) {
+                testScope.each() { createGradleDep("testCompile", build, it) }
+            }
+            if (!providedScope.isEmpty()) {
+                providedScope.each() { createGradleDep("providedCompile", build, it) }
+            }
+            if (!systemScope.isEmpty()) {
+                systemScope.each() { createGradleDep("system", build, it) }
+            }
+            build.append("}\n")
+        }
+        return build.toString();
+    }
+
+    def compilerSettings = { project, indent ->
+        def configuration = plugin('maven-compiler-plugin', project).configuration
+        return "sourceCompatibility = ${configuration.source.text() ?: '1.5'}\n${indent}targetCompatibility = ${configuration.target.text() ?: '1.5'}\n"
+    }
+
+    def plugin = { artifactId, project ->
+        project.build.plugins.plugin.find { pluginTag ->
+            pluginTag.artifactId.text() == artifactId
+        }
+    }
+
+    def pluginGoal = { goalName, plugin ->
+        plugin.executions.execution.find { exec ->
+            exec.goals.goal.find { gl ->
+                gl.text().startsWith(goalName)
+            }
+        }
+    }
+
+    def packSources = { sourceSets ->
+        def sourceSetStr = ''
+        if (!sourceSets.empty) {
+            sourceSetStr = """task packageSources(type: Jar) {
+classifier = 'sources'
+"""
+            sourceSets.each { sourceSet ->
+                sourceSetStr += """from sourceSets.${sourceSet}.allSource
+"""
+            }
+            sourceSetStr += """
+}
+artifacts.archives packageSources"""
+        }
+        sourceSetStr
+    }
+
+
+    def packageTests = { project ->
+        def jarPlugin = plugin('maven-jar-plugin', project)
+        pluginGoal('test-jar', jarPlugin) ? """
+task packageTests(type: Jar) {
+  from sourceSets.test.output
+  classifier = 'tests'
+}
+artifacts.archives packageTests
+""" : ''
+    }
+
+    def packageSources = { project ->
+        def sourcePlugin = plugin('maven-source-plugin', project)
+        def sourceSets = []
+        if (sourcePlugin) {
+            if (pluginGoal('jar', sourcePlugin)) {
+                sourceSets += 'main'
+            } else if (pluginGoal('test-jar', sourcePlugin)) {
+                sourceSets += 'test'
+            }
+        }
+        packSources(sourceSets)
+    }
+
+    private boolean duplicateDependency(dependency, project, allProjects) {
+        def parentTag = project.parent
+        if (allProjects == null || parentTag.isEmpty()) {//simple project or no parent
+            return false;
+        } else {
+            def parent = allProjects.find {
+                it.groupId.equals(parentTag.groupId) && it.artifactId.equals(parentTag.artifactId)
+            }
+            def duplicate = parent.dependencies.dependency.any {
+                it.groupId.equals(dependency.groupId) && it.artifactId.equals(dependency.artifactId)
+            }
+            if (duplicate) {
+                return true;
+            } else {
+                duplicateDependency(dependency, parent, allProjects)
+            }
+        }
+    }
+
+    def artifactId = { File dir ->
+        return new XmlSlurper().parse(new File(dir, 'pom.xml')).artifactId.text()
+    }
+
+    def projectDir = { project ->
+        return new File(project.build.directory.text()).parentFile
+    }
+
+    private def generateSettings(def dirName, def mvnProjectName, def projects) {
+        def qualifiedNames = [:]
+        def projectName = "";
+        if (dirName != mvnProjectName) {
+            projectName = """rootProject.name = '${mvnProjectName}'
+"""
+        }
+        def modulePoms = modules(projects, true)
+
+        def modules = new StringBuilder();
+        def artifactIdToDir = [:]
+        if (projects) {
+            modulePoms.each { project ->
+                def fqn = fqn(project, projects)
+                artifactIdToDir[fqn] = GFileUtils.relativePath(workingDir, projectDir(project))
+                modules.append("'${fqn}', ")
+            }
+            def strLength = modules.length()
+            if (strLength > 2) {
+                modules.delete(strLength - 2, strLength)
+            }
+        }
+        File settingsFile = new File("settings.gradle")
+        if (settingsFile.exists()) {
+            settingsFile.renameTo(new File("settings.gradle.bak"))
+        }
+        def settingsText = "${projectName}${modules.length() > 0 ? "include ${modules.toString()}" : ''}\n"
+        artifactIdToDir.each { entry ->
+            settingsText += """
+project('$entry.key').projectDir = """ + '"$rootDir/' + "${entry.value}" + '" as File'
+        }
+        settingsFile.text = settingsText
+        return qualifiedNames
+    }
+
+/**
+ * complex print statement does one extra task which is
+ * iterate over each <exclusion> node and print out the artifact id.
+ * It also provides review comments for the user.
+ */
+    private def createComplexDependency(it, build, scope, providedMessage) {
+        build.append("    ${scope}(${contructSignature(it)}) {\n")
+        it.exclusions.exclusion.each() {
+            build.append("exclude(module: '${it.artifactId}')\n")
+        }
+        if (providedMessage) {
+            build.append(providedMessage)
+        }
+        build.append("    }\n")
+    }
+
+/**
+ * Print out the basic form og gradle dependency
+ */
+    private def createBasicDependency(mavenDependency, build, String scope) {
+        def classifier = contructSignature(mavenDependency)
+        build.append("    ${scope} ${classifier}\n")
+    }
+/**
+ * Print out the basic form of gradle dependency
+ */
+    private def createProjectDependency(projectDep, build, String scope, allProjects) {
+        if (projectDep.packaging.text() == 'war') {
+            dependentWars += projectDep
+        }
+        build.append("  ${scope} project('${fqn(projectDep, allProjects)}')\n")
+    }
+
+/**
+ * Construct and return the signature of a dependency, including its version and
+ * classifier if it exists
+ */
+    private def contructSignature(mavenDependency) {
+        def gradelDep = "group: '${mavenDependency.groupId.text()}', name: '${mavenDependency.artifactId.text()}', version:'${mavenDependency?.version?.text()}'"
+        def classifier = elementHasText(mavenDependency.classifier) ? gradelDep + ", classifier:'" + mavenDependency.classifier.text().trim() + "'" : gradelDep
+        return classifier
+    }
+
+/**
+ * Check to see if the selected node has content
+ */
+    private boolean elementHasText(it) {
+        return it.text().length() != 0
+    }
+}
\ No newline at end of file
diff --git a/subprojects/build-setup/src/main/groovy/org/gradle/buildsetup/plugins/internal/maven/MavenConversionException.java b/subprojects/build-setup/src/main/groovy/org/gradle/buildsetup/plugins/internal/maven/MavenConversionException.java
new file mode 100644
index 0000000..aaf741b
--- /dev/null
+++ b/subprojects/build-setup/src/main/groovy/org/gradle/buildsetup/plugins/internal/maven/MavenConversionException.java
@@ -0,0 +1,29 @@
+/*
+ * Copyright 2012 the original author or authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.gradle.buildsetup.plugins.internal.maven;
+import org.gradle.api.internal.Contextual;
+
+ at Contextual
+public class MavenConversionException extends RuntimeException {
+    public MavenConversionException(String message) {
+        super(message);
+    }
+
+    public MavenConversionException(String message, Throwable cause) {
+        super(message, cause);    
+    }
+}
diff --git a/subprojects/build-setup/src/main/groovy/org/gradle/buildsetup/plugins/internal/maven/MavenProjectXmlWriter.java b/subprojects/build-setup/src/main/groovy/org/gradle/buildsetup/plugins/internal/maven/MavenProjectXmlWriter.java
new file mode 100644
index 0000000..e0620a3
--- /dev/null
+++ b/subprojects/build-setup/src/main/groovy/org/gradle/buildsetup/plugins/internal/maven/MavenProjectXmlWriter.java
@@ -0,0 +1,58 @@
+/*
+ * Copyright 2012 the original author or authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.gradle.buildsetup.plugins.internal.maven;
+
+import org.gradle.mvn3.org.apache.maven.model.io.xpp3.MavenXpp3Writer;
+import org.gradle.mvn3.org.apache.maven.project.MavenProject;
+
+import java.io.ByteArrayOutputStream;
+import java.io.IOException;
+import java.util.Set;
+
+public class MavenProjectXmlWriter {
+
+    //TODO this class attempts to mimic the behavior of the output of mvn help:effective-pom
+    //instead of this class we should walk the maven project object model (instead of parsing the xml!)
+
+    String toXml(Set<MavenProject> projects) {
+        assert !projects.isEmpty() : "Cannot prepare the Maven projects effective XML because provided projects set is empty.";
+
+        if (projects.size() == 1) {
+            return toXml(projects.iterator().next());
+        }
+
+        StringBuilder out = new StringBuilder("<projects>");
+        for (MavenProject project : projects) {
+            out.append(toXml(project));
+        }
+        return out.append("</projects>").toString();
+    }
+
+    private String toXml(MavenProject project) {
+        ByteArrayOutputStream out = new ByteArrayOutputStream();
+        try {
+            new MavenXpp3Writer().write(out, project.getModel());
+        } catch (IOException e) {
+            throw new RuntimeException("Unable to serialize Maven model to XML. Maven project: " + project, e);
+        }
+        return prepareXml(out.toString());
+    }
+
+    String prepareXml(String xml) {
+        return xml.replaceFirst("^<\\?xml.+?\\?>", "");
+    }
+}
diff --git a/subprojects/build-setup/src/main/groovy/org/gradle/buildsetup/plugins/internal/maven/MavenProjectsCreator.java b/subprojects/build-setup/src/main/groovy/org/gradle/buildsetup/plugins/internal/maven/MavenProjectsCreator.java
new file mode 100644
index 0000000..1fd32e5
--- /dev/null
+++ b/subprojects/build-setup/src/main/groovy/org/gradle/buildsetup/plugins/internal/maven/MavenProjectsCreator.java
@@ -0,0 +1,92 @@
+/*
+ * Copyright 2012 the original author or authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.gradle.buildsetup.plugins.internal.maven;
+
+import com.google.common.collect.ImmutableList;
+import org.gradle.api.Transformer;
+import org.gradle.mvn3.org.apache.maven.execution.*;
+import org.gradle.mvn3.org.apache.maven.project.*;
+import org.gradle.mvn3.org.apache.maven.settings.Settings;
+import org.gradle.mvn3.org.codehaus.plexus.ContainerConfiguration;
+import org.gradle.mvn3.org.codehaus.plexus.DefaultContainerConfiguration;
+import org.gradle.mvn3.org.codehaus.plexus.DefaultPlexusContainer;
+import org.gradle.mvn3.org.codehaus.plexus.PlexusContainerException;
+import org.gradle.mvn3.org.codehaus.plexus.classworlds.ClassWorld;
+import org.gradle.mvn3.org.codehaus.plexus.component.repository.exception.ComponentLookupException;
+import org.gradle.mvn3.org.codehaus.plexus.configuration.PlexusConfigurationException;
+import org.gradle.mvn3.org.sonatype.aether.RepositorySystemSession;
+import org.gradle.mvn3.org.sonatype.aether.util.DefaultRepositorySystemSession;
+import org.gradle.util.CollectionUtils;
+
+import java.io.File;
+import java.util.LinkedHashSet;
+import java.util.List;
+import java.util.Set;
+
+public class MavenProjectsCreator {
+
+    public Set<MavenProject> create(Settings mavenSettings, File pomFile) {
+        if (!pomFile.exists()) {
+            throw new MavenConversionException(String.format("Unable to create Maven project model. The POM file %s does not exist.", pomFile));
+        }
+        try {
+            return createNow(mavenSettings, pomFile);
+        } catch (Exception e) {
+            throw new MavenConversionException(String.format("Unable to create Maven project model using POM %s.", pomFile), e);
+        }
+    }
+
+    private Set<MavenProject> createNow(Settings settings, File pomFile) throws PlexusContainerException, PlexusConfigurationException, ComponentLookupException, MavenExecutionRequestPopulationException, ProjectBuildingException {
+        //using jarjar for maven3 classes affects the contents of the effective pom
+        //references to certain Maven standard plugins contain jarjar in the fqn
+        //not sure if this is a problem.
+        ContainerConfiguration containerConfiguration = new DefaultContainerConfiguration()
+                .setClassWorld(new ClassWorld("plexus.core", ClassWorld.class.getClassLoader()))
+                .setName("mavenCore");
+
+        DefaultPlexusContainer container = new DefaultPlexusContainer(containerConfiguration);
+        ProjectBuilder builder = container.lookup(ProjectBuilder.class);
+        MavenExecutionRequest executionRequest = new DefaultMavenExecutionRequest();
+        MavenExecutionRequestPopulator populator = container.lookup(MavenExecutionRequestPopulator.class);
+        populator.populateFromSettings(executionRequest, settings);
+        populator.populateDefaults(executionRequest);
+        ProjectBuildingRequest buildingRequest = executionRequest.getProjectBuildingRequest();
+        buildingRequest.setProcessPlugins(false);
+        MavenProject mavenProject = builder.build(pomFile, buildingRequest).getProject();
+        Set<MavenProject> reactorProjects = new LinkedHashSet<MavenProject>();
+
+        //TODO adding the parent project first because the converter needs it this way ATM. This is oversimplified.
+        //the converter should not depend on the order of reactor projects.
+        //we should add coverage for nested multi-project builds with multiple parents.
+        reactorProjects.add(mavenProject);
+
+        List<ProjectBuildingResult> allProjects = builder.build(ImmutableList.of(pomFile), true, buildingRequest);
+        CollectionUtils.collect(allProjects, reactorProjects, new Transformer<MavenProject, ProjectBuildingResult>() {
+            public MavenProject transform(ProjectBuildingResult original) {
+                return original.getProject();
+            }
+        });
+
+        MavenExecutionResult result = new DefaultMavenExecutionResult();
+        result.setProject(mavenProject);
+        RepositorySystemSession repoSession = new DefaultRepositorySystemSession();
+        MavenSession session = new MavenSession(container, repoSession, executionRequest, result);
+        session.setCurrentProject(mavenProject);
+
+        return reactorProjects;
+    }
+}
diff --git a/subprojects/build-setup/src/main/groovy/org/gradle/buildsetup/tasks/SetupBuild.groovy b/subprojects/build-setup/src/main/groovy/org/gradle/buildsetup/tasks/SetupBuild.groovy
new file mode 100644
index 0000000..b27e649
--- /dev/null
+++ b/subprojects/build-setup/src/main/groovy/org/gradle/buildsetup/tasks/SetupBuild.groovy
@@ -0,0 +1,59 @@
+/*
+ * Copyright 2013 the original author or authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.gradle.buildsetup.tasks
+
+import org.gradle.api.DefaultTask
+import org.gradle.api.GradleException
+import org.gradle.api.Incubating
+import org.gradle.api.internal.tasks.CommandLineOption
+import org.gradle.api.tasks.TaskAction
+import org.gradle.buildsetup.plugins.internal.BuildSetupTypeIds
+import org.gradle.buildsetup.plugins.internal.ProjectLayoutSetupRegistry
+
+/**
+ * Generates a Gradle project structure.
+  */
+ at Incubating
+class SetupBuild extends DefaultTask {
+    private String type
+
+    ProjectLayoutSetupRegistry projectLayoutRegistry
+
+    /**
+     * The desired type of build to create, defaults to {@value BuildSetupTypeIds#POM} if 'pom.xml' is found in project root
+     * if no pom.xml is found, it defaults to {@value BuildSetupTypeIds#BASIC}.
+     *
+     * This property can be set via command-line option '--type'.
+     */
+    String getType() {
+        type ?: project.file("pom.xml").exists() ? BuildSetupTypeIds.POM : BuildSetupTypeIds.BASIC
+    }
+
+    @TaskAction
+    void setupProjectLayout() {
+        def type = getType()
+        if (!projectLayoutRegistry.supports(type)) {
+            throw new GradleException("The requested build setup type '${type}' is not supported. Supported types: ${projectLayoutRegistry.all.collect { "'${it.id}'" }.join(", ")}.")
+        }
+        projectLayoutRegistry.get(type).generateProject()
+    }
+
+    @CommandLineOption(options = "type", description = "Set type of build to create.")
+    public void setType(String type) {
+        this.type = type;
+    }
+}
diff --git a/subprojects/build-setup/src/main/resources/META-INF/gradle-plugins/build-setup.properties b/subprojects/build-setup/src/main/resources/META-INF/gradle-plugins/build-setup.properties
new file mode 100644
index 0000000..bb78f47
--- /dev/null
+++ b/subprojects/build-setup/src/main/resources/META-INF/gradle-plugins/build-setup.properties
@@ -0,0 +1 @@
+implementation-class=org.gradle.buildsetup.plugins.BuildSetupPlugin
diff --git a/subprojects/build-setup/src/main/resources/META-INF/gradle-plugins/wrapper.properties b/subprojects/build-setup/src/main/resources/META-INF/gradle-plugins/wrapper.properties
new file mode 100644
index 0000000..5468281
--- /dev/null
+++ b/subprojects/build-setup/src/main/resources/META-INF/gradle-plugins/wrapper.properties
@@ -0,0 +1,17 @@
+#
+# Copyright 2013 the original author or authors.
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+#      http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT 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.buildsetup.plugins.WrapperPlugin
diff --git a/subprojects/build-setup/src/main/resources/META-INF/services/org.gradle.configuration.project.ProjectConfigureAction b/subprojects/build-setup/src/main/resources/META-INF/services/org.gradle.configuration.project.ProjectConfigureAction
new file mode 100644
index 0000000..be1a2c0
--- /dev/null
+++ b/subprojects/build-setup/src/main/resources/META-INF/services/org.gradle.configuration.project.ProjectConfigureAction
@@ -0,0 +1,2 @@
+org.gradle.buildsetup.plugins.internal.BuildSetupAutoApplyAction
+org.gradle.buildsetup.plugins.internal.WrapperPluginAutoApplyAction
diff --git a/subprojects/build-setup/src/main/resources/org/gradle/buildsetup/tasks/templates/Library.java.template b/subprojects/build-setup/src/main/resources/org/gradle/buildsetup/tasks/templates/Library.java.template
new file mode 100644
index 0000000..9e451aa
--- /dev/null
+++ b/subprojects/build-setup/src/main/resources/org/gradle/buildsetup/tasks/templates/Library.java.template
@@ -0,0 +1,11 @@
+/*
+ * This Java source file was auto generated by running 'gradle buildSetup --type java-library'
+ * by '${genUser.groovyComment}' at '${genDate.groovyComment}' with ${genGradleVersion.groovyComment}
+ *
+ * @author ${genUser.groovyComment}, @date ${genDate.groovyComment}
+ */
+public class Library {
+    public boolean someLibraryMethod() {
+        return true;
+    }
+}
diff --git a/subprojects/build-setup/src/main/resources/org/gradle/buildsetup/tasks/templates/LibraryTest.java.template b/subprojects/build-setup/src/main/resources/org/gradle/buildsetup/tasks/templates/LibraryTest.java.template
new file mode 100644
index 0000000..17ed8f5
--- /dev/null
+++ b/subprojects/build-setup/src/main/resources/org/gradle/buildsetup/tasks/templates/LibraryTest.java.template
@@ -0,0 +1,15 @@
+import org.junit.Test;
+import static org.junit.Assert.*;
+
+/*
+ * This Java source file was auto generated by running 'gradle buildSetup --type java-library'
+ * by '${genUser.groovyComment}' at '${genDate.groovyComment}' with ${genGradleVersion.groovyComment}
+ *
+ * @author ${genUser.groovyComment}, @date ${genDate.groovyComment}
+ */
+public class LibraryTest {
+    @Test public void testSomeLibraryMethod() {
+        Library classUnderTest = new Library();
+        assertTrue("someLibraryMethod should return 'true'", classUnderTest.someLibraryMethod());
+    }
+}
diff --git a/subprojects/build-setup/src/main/resources/org/gradle/buildsetup/tasks/templates/build.gradle.template b/subprojects/build-setup/src/main/resources/org/gradle/buildsetup/tasks/templates/build.gradle.template
new file mode 100644
index 0000000..7bcd658
--- /dev/null
+++ b/subprojects/build-setup/src/main/resources/org/gradle/buildsetup/tasks/templates/build.gradle.template
@@ -0,0 +1,32 @@
+/*
+ * This build file was auto generated by running the Gradle 'buildSetup' task
+ * by '${genUser.groovyComment}' at '${genDate.groovyComment}' with ${genGradleVersion.groovyComment}
+ *
+ * This generated file contains a commented-out sample Java project to get you started.
+ * For more details take a look at the Java Quickstart chapter in the Gradle
+ * user guide available at ${ref_userguide_java_tutorial.groovyComment}
+ */
+
+/*
+// Apply the java plugin to add support for Java
+apply plugin: 'java'
+
+// In this section you declare where to find the dependencies of your project
+repositories {
+    // Use 'maven central' for resolving your dependencies.
+    // You can declare any Maven/Ivy/file repository here.
+    mavenCentral()
+}
+
+// In this section you declare the dependencies for your production and test code
+dependencies {
+    // The production code uses the SLF4J logging API at compile time
+    compile 'org.slf4j:slf4j-api:1.7.5'
+
+    // Declare the dependency for your favourite test framework you want to use in your tests.
+    // TestNG is also supported by the Gradle Test task. Just change the
+    // testCompile dependency to testCompile 'org.testng:testng:6.8.1' and add
+    // 'test.useTestNG()' to your build script.
+    testCompile "junit:junit:4.11"
+}
+*/
diff --git a/subprojects/build-setup/src/main/resources/org/gradle/buildsetup/tasks/templates/java-library-build.gradle.template b/subprojects/build-setup/src/main/resources/org/gradle/buildsetup/tasks/templates/java-library-build.gradle.template
new file mode 100644
index 0000000..74f2e4b
--- /dev/null
+++ b/subprojects/build-setup/src/main/resources/org/gradle/buildsetup/tasks/templates/java-library-build.gradle.template
@@ -0,0 +1,30 @@
+/*
+ * This build file was auto generated by running the Gradle 'buildSetup' task
+ * by '${genUser.groovyComment}' at '${genDate.groovyComment}' with ${genGradleVersion.groovyComment}
+ *
+ * This generated file contains a sample Java project to get you started.
+ * For more details take a look at the Java Quickstart chapter in the Gradle
+ * user guide available at ${ref_userguide_java_tutorial.groovyComment}
+ */
+
+// Apply the java plugin to add support for Java
+apply plugin: 'java'
+
+// In this section you declare where to find the dependencies of your project
+repositories {
+    // Use 'maven central' for resolving your dependencies.
+    // You can declare any Maven/Ivy/file repository here.
+    mavenCentral()
+}
+
+// In this section you declare the dependencies for your production and test code
+dependencies {
+    // The production code uses the SLF4J logging API at compile time
+    compile 'org.slf4j:slf4j-api:1.7.5'
+
+    // Declare the dependency for your favourite test framework you want to use in your tests.
+    // TestNG is also supported by the Gradle Test task. Just change the
+    // testCompile dependency to testCompile 'org.testng:testng:6.8.1' and add
+    // 'test.useTestNG()' to your build script.
+    testCompile "junit:junit:4.11"
+}
diff --git a/subprojects/build-setup/src/main/resources/org/gradle/buildsetup/tasks/templates/settings.gradle.template b/subprojects/build-setup/src/main/resources/org/gradle/buildsetup/tasks/templates/settings.gradle.template
new file mode 100644
index 0000000..1dd3b47
--- /dev/null
+++ b/subprojects/build-setup/src/main/resources/org/gradle/buildsetup/tasks/templates/settings.gradle.template
@@ -0,0 +1,19 @@
+/*
+ * This settings file was auto generated by the Gradle buildSetup task
+ * by '${genUser.groovyComment}' at '${genDate.groovyComment}' with ${genGradleVersion.groovyComment}
+ *
+ * The settings file is used to specify which projects to include in your build.
+ * In a single project build this file can be empty or even removed.
+ *
+ * Detailed information about configuring a multi-project build in Gradle can be found
+ * in the user guide at ${ref_userguide_multiproject.groovyComment}
+ */
+
+/*
+// To declare projects as part of a multi-project build use the 'include' method
+include 'shared'
+include 'api'
+include 'services:webservice'
+*/
+
+rootProject.name = '${rootProjectName.groovyString}'
diff --git a/subprojects/build-setup/src/test/groovy/org/gradle/api/tasks/wrapper/WrapperTest.java b/subprojects/build-setup/src/test/groovy/org/gradle/api/tasks/wrapper/WrapperTest.java
new file mode 100644
index 0000000..bf46f0c
--- /dev/null
+++ b/subprojects/build-setup/src/test/groovy/org/gradle/api/tasks/wrapper/WrapperTest.java
@@ -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.api.tasks.wrapper;
+
+import org.gradle.api.internal.AbstractTask;
+import org.gradle.api.tasks.AbstractTaskTest;
+import org.gradle.test.fixtures.file.TestFile;
+import org.gradle.test.fixtures.file.TestNameTestDirectoryProvider;
+import org.gradle.util.GUtil;
+import org.gradle.util.GradleVersion;
+import org.gradle.util.WrapUtil;
+import org.gradle.wrapper.GradleWrapperMain;
+import org.gradle.wrapper.WrapperExecutor;
+import org.junit.Before;
+import org.junit.Rule;
+import org.junit.Test;
+
+import java.io.File;
+import java.io.IOException;
+import java.util.Properties;
+
+import static org.hamcrest.Matchers.equalTo;
+import static org.junit.Assert.*;
+
+public class WrapperTest extends AbstractTaskTest {
+
+    private Wrapper wrapper;
+    private String targetWrapperJarPath;
+    private TestFile expectedTargetWrapperJar;
+    private File expectedTargetWrapperProperties;
+    @Rule
+    public TestNameTestDirectoryProvider tmpDir = new TestNameTestDirectoryProvider();
+
+    @Before
+    public void setUp() {
+        wrapper = createTask(Wrapper.class);
+        wrapper.setGradleVersion("1.0");
+        targetWrapperJarPath = "gradle/wrapper";
+        expectedTargetWrapperJar = new TestFile(getProject().getProjectDir(),
+                targetWrapperJarPath + "/gradle-wrapper.jar");
+        expectedTargetWrapperProperties = new File(getProject().getProjectDir(),
+                targetWrapperJarPath + "/gradle-wrapper.properties");
+        new File(getProject().getProjectDir(), targetWrapperJarPath).mkdirs();
+        wrapper.setDistributionPath("somepath");
+    }
+
+    public AbstractTask getTask() {
+        return wrapper;
+    }
+
+    @Test
+    public void testWrapperDefaults() {
+        wrapper = createTask(Wrapper.class);
+        assertEquals(new File(getProject().getProjectDir(), "gradle/wrapper/gradle-wrapper.jar"), wrapper.getJarFile());
+        assertEquals(new File(getProject().getProjectDir(), "gradlew"), wrapper.getScriptFile());
+        assertEquals(new File(getProject().getProjectDir(), "gradlew.bat"), wrapper.getBatchScript());
+        assertEquals(GradleVersion.current().getVersion(), wrapper.getGradleVersion());
+        assertEquals(Wrapper.DEFAULT_DISTRIBUTION_PARENT_NAME, wrapper.getDistributionPath());
+        assertEquals(Wrapper.DEFAULT_DISTRIBUTION_PARENT_NAME, wrapper.getArchivePath());
+        assertEquals(Wrapper.PathBase.GRADLE_USER_HOME, wrapper.getDistributionBase());
+        assertEquals(Wrapper.PathBase.GRADLE_USER_HOME, wrapper.getArchiveBase());
+        assertNotNull(wrapper.getDistributionUrl());
+    }
+
+    @Test
+    public void testDeterminesWindowsScriptPathFromUnixScriptPath() {
+        wrapper.setScriptFile("build/gradle.sh");
+        assertEquals(getProject().file("build/gradle.bat"), wrapper.getBatchScript());
+
+        wrapper.setScriptFile("build/gradle-wrapper");
+        assertEquals(getProject().file("build/gradle-wrapper.bat"), wrapper.getBatchScript());
+    }
+
+    @Test
+    public void testDeterminesPropertiesFilePathFromJarPath() {
+        wrapper.setJarFile("build/gradle-wrapper.jar");
+        assertEquals(getProject().file("build/gradle-wrapper.properties"), wrapper.getPropertiesFile());
+    }
+    
+    @Test
+    public void testDownloadsFromReleaseRepositoryForReleaseVersions() {
+        wrapper.setGradleVersion("0.9.1");
+        assertEquals("http://services.gradle.org/distributions/gradle-0.9.1-bin.zip", wrapper.getDistributionUrl());
+    }
+
+    @Test
+    public void testDownloadsFromReleaseRepositoryForPreviewReleaseVersions() {
+        wrapper.setGradleVersion("1.0-milestone-1");
+        assertEquals("http://services.gradle.org/distributions/gradle-1.0-milestone-1-bin.zip", wrapper.getDistributionUrl());
+    }
+
+    @Test
+    public void testDownloadsFromSnapshotRepositoryForSnapshotVersions() {
+        wrapper.setGradleVersion("0.9.1-20101224110000+1100");
+        assertEquals("http://services.gradle.org/distributions-snapshots/gradle-0.9.1-20101224110000+1100-bin.zip", wrapper.getDistributionUrl());
+    }
+
+    @Test
+    public void testUsesExplicitlyDefinedDistributionUrl() {
+        wrapper.setGradleVersion("0.9");
+        wrapper.setDistributionUrl("http://some-url");
+        assertEquals("http://some-url", wrapper.getDistributionUrl());
+    }
+
+    @Test
+    public void testExecuteWithNonExistingWrapperJarParentDir() throws IOException {
+        checkExecute();
+    }
+
+    @Test
+    public void testCheckInputs() throws IOException {
+        assertThat(wrapper.getInputs().getProperties().keySet(),
+                equalTo(WrapUtil.toSet("distributionBase", "distributionPath", "distributionUrl", "archiveBase", "archivePath")));
+    }
+
+    @Test
+    public void testExecuteWithExistingWrapperJarParentDirAndExistingWrapperJar() throws IOException {
+        File jarDir = new File(getProject().getProjectDir(), "lib");
+        jarDir.mkdirs();
+        File wrapperJar = new File(getProject().getProjectDir(), targetWrapperJarPath);
+        File parentFile = expectedTargetWrapperJar.getParentFile();
+        assertTrue(parentFile.isDirectory() || parentFile.mkdirs());
+        try {
+            assertTrue(expectedTargetWrapperJar.createNewFile());
+        } catch (IOException e) {
+            throw new RuntimeException(String.format("Could not create %s.", wrapperJar), e);
+        }
+        checkExecute();
+    }
+
+    private void checkExecute() throws IOException {
+        wrapper.execute();
+        TestFile unjarDir = tmpDir.createDir("unjar");
+        expectedTargetWrapperJar.unzipTo(unjarDir);
+        unjarDir.file(GradleWrapperMain.class.getName().replace(".", "/") + ".class").assertIsFile();
+        Properties properties = GUtil.loadProperties(expectedTargetWrapperProperties);
+        assertEquals(properties.getProperty(WrapperExecutor.DISTRIBUTION_URL_PROPERTY), wrapper.getDistributionUrl());
+        assertEquals(properties.getProperty(WrapperExecutor.DISTRIBUTION_BASE_PROPERTY), wrapper.getDistributionBase().toString());
+        assertEquals(properties.getProperty(WrapperExecutor.DISTRIBUTION_PATH_PROPERTY), wrapper.getDistributionPath());
+        assertEquals(properties.getProperty(WrapperExecutor.ZIP_STORE_BASE_PROPERTY), wrapper.getArchiveBase().toString());
+        assertEquals(properties.getProperty(WrapperExecutor.ZIP_STORE_PATH_PROPERTY), wrapper.getArchivePath());
+    }
+
+    private String toNative(String s) {
+        return s.replace("/", File.separator);
+    }
+}
diff --git a/subprojects/build-setup/src/test/groovy/org/gradle/buildsetup/plugins/BuildSetupPluginSpec.groovy b/subprojects/build-setup/src/test/groovy/org/gradle/buildsetup/plugins/BuildSetupPluginSpec.groovy
new file mode 100644
index 0000000..18fbbb4
--- /dev/null
+++ b/subprojects/build-setup/src/test/groovy/org/gradle/buildsetup/plugins/BuildSetupPluginSpec.groovy
@@ -0,0 +1,77 @@
+/*
+ * Copyright 2012 the original author or authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.gradle.buildsetup.plugins
+
+import org.gradle.api.internal.file.TemporaryFileProvider
+import org.gradle.api.internal.file.TmpDirTemporaryFileProvider
+import org.gradle.api.tasks.wrapper.Wrapper
+import org.gradle.util.TestUtil
+import org.gradle.util.Matchers
+import spock.lang.Specification
+
+class BuildSetupPluginSpec extends Specification {
+    def project = TestUtil.createRootProject()
+
+    def "applies plugin"() {
+        when:
+        project.plugins.apply BuildSetupPlugin
+        and:
+        project.evaluate()
+        then:
+        project.tasks.wrapper instanceof Wrapper
+        Matchers.dependsOn("wrapper").matches(project.tasks.setupBuild)
+    }
+
+    def "no wrapper task configured if build file already exists"() {
+        setup:
+        TemporaryFileProvider temporaryFileProvider = new TmpDirTemporaryFileProvider();
+        File projectDir = temporaryFileProvider.createTemporaryDirectory("gradle", "projectDir");
+        def buildFile = new File(projectDir, "build.gradle") << '// an empty build'
+        buildFile << '// an empty build'
+        project = TestUtil.builder().withProjectDir(projectDir).build()
+        when:
+        project.plugins.apply BuildSetupPlugin
+
+        then:
+        project.setupBuild != null
+        project.tasks.collect { it.name } == ["setupBuild"]
+    }
+
+    def "no build file generation if settings file already exists"() {
+        setup:
+        project.file("settings.gradle") << '// an empty file'
+
+        when:
+        project.plugins.apply BuildSetupPlugin
+
+        then:
+        project.setupBuild != null
+        project.tasks.collect { it.name } == ["setupBuild"]
+    }
+
+    def "no build file generation when part of multi-project build"() {
+        setup:
+        TestUtil.createChildProject(project, 'child')
+
+        when:
+        project.plugins.apply BuildSetupPlugin
+
+        then:
+        project.setupBuild != null
+        project.tasks.collect { it.name } == ["setupBuild"]
+    }
+}
diff --git a/subprojects/build-setup/src/test/groovy/org/gradle/buildsetup/plugins/WrapperPluginSpec.groovy b/subprojects/build-setup/src/test/groovy/org/gradle/buildsetup/plugins/WrapperPluginSpec.groovy
new file mode 100644
index 0000000..91dfeab
--- /dev/null
+++ b/subprojects/build-setup/src/test/groovy/org/gradle/buildsetup/plugins/WrapperPluginSpec.groovy
@@ -0,0 +1,36 @@
+/*
+ * Copyright 2013 the original author or authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+
+
+package org.gradle.buildsetup.plugins
+
+import org.gradle.api.tasks.wrapper.Wrapper
+import org.gradle.util.TestUtil
+import spock.lang.Specification
+
+class WrapperPluginSpec extends Specification {
+    def project = TestUtil.createRootProject()
+
+    def "adds 'wrapper' task"() {
+        when:
+        project.plugins.apply WrapperPlugin
+
+        then:
+        project.tasks.wrapper instanceof Wrapper
+        project.tasks.wrapper.group == BuildSetupPlugin.GROUP
+    }
+}
diff --git a/subprojects/build-setup/src/test/groovy/org/gradle/buildsetup/plugins/internal/BuildSetupAutoApplyActionSpec.groovy b/subprojects/build-setup/src/test/groovy/org/gradle/buildsetup/plugins/internal/BuildSetupAutoApplyActionSpec.groovy
new file mode 100644
index 0000000..b7fcc74
--- /dev/null
+++ b/subprojects/build-setup/src/test/groovy/org/gradle/buildsetup/plugins/internal/BuildSetupAutoApplyActionSpec.groovy
@@ -0,0 +1,72 @@
+/*
+ * Copyright 2013 the original author or authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.gradle.buildsetup.plugins.internal
+
+import org.gradle.api.internal.project.ProjectInternal
+import org.gradle.api.internal.tasks.TaskContainerInternal
+import org.gradle.api.plugins.PluginContainer
+import org.gradle.test.fixtures.file.TestFile
+import org.gradle.test.fixtures.file.TestNameTestDirectoryProvider
+import org.junit.Rule
+import spock.lang.Specification
+
+class BuildSetupAutoApplyActionSpec extends Specification {
+
+    @Rule
+    final TestNameTestDirectoryProvider temporaryFolder = new TestNameTestDirectoryProvider()
+    TestFile buildFile
+    ProjectInternal projectInternal
+    PluginContainer pluginContainer
+    TaskContainerInternal taskContainerInternal
+
+    public void setup() {
+        projectInternal = Mock(ProjectInternal)
+        pluginContainer = Mock(PluginContainer)
+        taskContainerInternal = Mock(TaskContainerInternal)
+        _ * projectInternal.getTasks() >> taskContainerInternal
+
+    }
+
+    def "applies placeholder action for setupBuild on taskcontainer"() {
+        when:
+        new BuildSetupAutoApplyAction().execute(projectInternal)
+        then:
+        1 * taskContainerInternal.addPlaceholderAction("setupBuild", _) >> {args -> args[1].run()}
+        1 * projectInternal.getParent() >> null
+        1 * projectInternal.getPlugins() >> pluginContainer
+        1 * pluginContainer.apply("build-setup")
+    }
+
+    def "is not applied on non rootprojects"() {
+        given:
+        isNotRootProject()
+        when:
+        new BuildSetupAutoApplyAction().execute(projectInternal)
+        then:
+        0 * taskContainerInternal.addPlaceholderAction("setupBuild", _)
+        0 * projectInternal.getPlugins() >> pluginContainer
+        0 * pluginContainer.apply("build-setup")
+    }
+
+    def isNotRootProject() {
+        projectInternal.getParent() >> Mock(ProjectInternal)
+    }
+
+    void noChildprojects() {
+        projectInternal.subprojects >> []
+    }
+}
diff --git a/subprojects/build-setup/src/test/groovy/org/gradle/buildsetup/plugins/internal/ProjectLayoutSetupRegistryFactoryTest.groovy b/subprojects/build-setup/src/test/groovy/org/gradle/buildsetup/plugins/internal/ProjectLayoutSetupRegistryFactoryTest.groovy
new file mode 100644
index 0000000..4efbaa9
--- /dev/null
+++ b/subprojects/build-setup/src/test/groovy/org/gradle/buildsetup/plugins/internal/ProjectLayoutSetupRegistryFactoryTest.groovy
@@ -0,0 +1,52 @@
+/*
+ * Copyright 2013 the original author or authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.gradle.buildsetup.plugins.internal
+
+import org.gradle.api.internal.DocumentationRegistry
+import org.gradle.api.internal.artifacts.mvnsettings.MavenSettingsProvider
+import org.gradle.api.internal.file.FileResolver
+import spock.lang.Specification
+import spock.lang.Unroll
+
+class ProjectLayoutSetupRegistryFactoryTest extends Specification {
+
+    ProjectLayoutSetupRegistryFactory projectLayoutSetupRegistry
+    DocumentationRegistry documentationRegistry
+    MavenSettingsProvider mavenSettingsProvider
+    FileResolver fileResolver
+
+    def setup() {
+        fileResolver = Mock()
+        projectLayoutSetupRegistry = new ProjectLayoutSetupRegistryFactory(mavenSettingsProvider, documentationRegistry, fileResolver);
+    }
+
+    @Unroll
+    def "supports '#type' project descriptor type"() {
+        when:
+        ProjectSetupDescriptor descriptor = projectLayoutSetupRegistry.createProjectLayoutSetupRegistry().get(type)
+
+        then:
+        descriptor != null
+        descriptor.class == clazz
+
+        where:
+        type                           | clazz
+        BuildSetupTypeIds.POM          | PomProjectSetupDescriptor.class
+        BuildSetupTypeIds.BASIC        | BasicProjectSetupDescriptor.class
+        BuildSetupTypeIds.JAVA_LIBRARY | JavaLibraryProjectSetupDescriptor.class
+    }
+}
diff --git a/subprojects/build-setup/src/test/groovy/org/gradle/buildsetup/plugins/internal/ProjectLayoutSetupRegistrySpec.groovy b/subprojects/build-setup/src/test/groovy/org/gradle/buildsetup/plugins/internal/ProjectLayoutSetupRegistrySpec.groovy
new file mode 100644
index 0000000..ff44192
--- /dev/null
+++ b/subprojects/build-setup/src/test/groovy/org/gradle/buildsetup/plugins/internal/ProjectLayoutSetupRegistrySpec.groovy
@@ -0,0 +1,58 @@
+/*
+ * Copyright 2013 the original author or authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.gradle.buildsetup.plugins.internal
+
+import org.gradle.api.GradleException
+import spock.lang.Specification
+
+
+class ProjectLayoutSetupRegistrySpec extends Specification {
+
+
+    ProjectLayoutSetupRegistry registry = new ProjectLayoutSetupRegistry()
+
+    def "can add multiple projectlayoutdescriptors"() {
+        when:
+        registry.add(descriptor("desc1"))
+        registry.add(descriptor("desc2"))
+        registry.add(descriptor("desc3"))
+        then:
+        registry.supports("desc1")
+        registry.get("desc1") != null
+
+        registry.supports("desc2")
+        registry.get("desc2") != null
+
+        registry.supports("desc3")
+        registry.get("desc3") != null
+    }
+
+    def "cannot add multiple descriptors with same id"() {
+        when:
+        registry.add(descriptor("desc1"))
+        registry.add(descriptor("desc1"))
+        then:
+        def e = thrown(GradleException)
+        e.message == "ProjectDescriptor with ID 'desc1' already registered."
+    }
+
+    ProjectSetupDescriptor descriptor(String descrName) {
+        ProjectSetupDescriptor descriptor = Mock()
+        _ * descriptor.id >> descrName
+        descriptor
+    }
+}
diff --git a/subprojects/build-setup/src/test/groovy/org/gradle/buildsetup/plugins/internal/TemplateBasedProjectSetupDescriptorSpec.groovy b/subprojects/build-setup/src/test/groovy/org/gradle/buildsetup/plugins/internal/TemplateBasedProjectSetupDescriptorSpec.groovy
new file mode 100644
index 0000000..804ab88
--- /dev/null
+++ b/subprojects/build-setup/src/test/groovy/org/gradle/buildsetup/plugins/internal/TemplateBasedProjectSetupDescriptorSpec.groovy
@@ -0,0 +1,105 @@
+/*
+ * Copyright 2013 the original author or authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.gradle.buildsetup.plugins.internal
+
+import org.gradle.api.internal.DocumentationRegistry
+import org.gradle.api.internal.file.FileResolver
+import org.gradle.test.fixtures.encoding.Identifier
+import org.gradle.test.fixtures.file.TestFile
+import org.gradle.test.fixtures.file.TestNameTestDirectoryProvider
+import org.junit.Rule
+import spock.lang.Specification
+
+class TemplateBasedProjectSetupDescriptorSpec extends Specification {
+
+    private TestTemplateBasedProjectSetupDescriptor descriptor
+    private DocumentationRegistry documentationRegistry
+    private FileResolver fileResolver
+    private TestFile buildTemplateFile
+    private TestFile settingsTemplateFile
+    private URL buildFileTemplateURL
+    private URL settingsTemplateURL
+    @Rule
+    final TestNameTestDirectoryProvider temporaryFolder = new TestNameTestDirectoryProvider()
+
+    def setup() {
+        buildTemplateFile = temporaryFolder.createFile("build.gradle.template")
+        buildFileTemplateURL = buildTemplateFile.toURI().toURL()
+
+        settingsTemplateFile = temporaryFolder.createFile("settings.gradle.template")
+        settingsTemplateFile << 'root'
+        settingsTemplateURL = settingsTemplateFile.toURI().toURL()
+
+        documentationRegistry = Mock(DocumentationRegistry)
+
+        fileResolver = Mock(FileResolver)
+        _ * fileResolver.resolve(_) >> {
+            temporaryFolder.file(it[0])
+        }
+
+        descriptor = new TestTemplateBasedProjectSetupDescriptor(fileResolver, documentationRegistry)
+    }
+
+    def "can generate build and settings file via templates"() {
+        when:
+        descriptor.generateProject()
+        then:
+        temporaryFolder.file("build.gradle").exists()
+        temporaryFolder.file("settings.gradle").exists()
+    }
+
+    def "escapes strings and encodes contents using UTF-8"() {
+        setup:
+        buildTemplateFile.text = '${somePath.groovyComment}'
+        settingsTemplateFile.text = '${someValue.groovyString}'
+        when:
+        descriptor.generateProject()
+        then:
+        temporaryFolder.file("build.gradle").getText('utf-8') == /C:\\Programe Files\\gradle/
+        temporaryFolder.file("settings.gradle").getText('utf-8') == new Identifier(/a\'b\\c/).withNonAscii().toString()
+    }
+
+    class TestTemplateBasedProjectSetupDescriptor extends TemplateBasedProjectSetupDescriptor {
+
+        public TestTemplateBasedProjectSetupDescriptor(FileResolver fileResolver, DocumentationRegistry documentationRegistry) {
+            super(fileResolver, documentationRegistry)
+        }
+
+        @Override
+        URL getBuildFileTemplate() {
+            return buildFileTemplateURL
+        }
+
+        @Override
+        URL getSettingsTemplate() {
+            return settingsTemplateURL
+        }
+
+        @Override
+        protected Map getAdditionalBuildFileTemplateBindings() {
+            return [somePath: "C:\\Programe Files\\gradle"]
+        }
+
+        protected Map getAdditionalSettingsFileTemplateBindings() {
+            return [someValue: new Identifier("a\'b\\c").withNonAscii().toString()]
+        }
+
+        String getId() {
+            return "test-ID"
+        }
+    }
+}
diff --git a/subprojects/build-setup/src/test/groovy/org/gradle/buildsetup/plugins/internal/TemplateValueTest.groovy b/subprojects/build-setup/src/test/groovy/org/gradle/buildsetup/plugins/internal/TemplateValueTest.groovy
new file mode 100644
index 0000000..32f9241
--- /dev/null
+++ b/subprojects/build-setup/src/test/groovy/org/gradle/buildsetup/plugins/internal/TemplateValueTest.groovy
@@ -0,0 +1,48 @@
+/*
+ * Copyright 2013 the original author or authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.gradle.buildsetup.plugins.internal
+
+import spock.lang.Specification
+
+class TemplateValueTest extends Specification {
+    def "escapes value for inclusion in a comment"() {
+        expect:
+        new TemplateValue(value).groovyComment == escaped
+
+        where:
+        value   | escaped
+        ''      | ''
+        'abc'   | 'abc'
+        'a\n\t' | 'a\n\t'
+        /a\b/   | /a\\b/
+    }
+
+    def "escapes value for inclusion in a string"() {
+        expect:
+        new TemplateValue(value).groovyString == escaped
+
+        where:
+        value         | escaped
+        ''            | ''
+        'abc'         | 'abc'
+        'a\n\t\r\b\f' | /a\n\t\r\b\f/
+        /a\b/         | /a\\b/
+        /'/           | /\'/
+        /"/           | /"/
+        /\'/          | /\\\'/
+    }
+}
diff --git a/subprojects/build-setup/src/test/groovy/org/gradle/buildsetup/plugins/internal/maven/MavenProjectXmlWriterTest.groovy b/subprojects/build-setup/src/test/groovy/org/gradle/buildsetup/plugins/internal/maven/MavenProjectXmlWriterTest.groovy
new file mode 100644
index 0000000..93b918b
--- /dev/null
+++ b/subprojects/build-setup/src/test/groovy/org/gradle/buildsetup/plugins/internal/maven/MavenProjectXmlWriterTest.groovy
@@ -0,0 +1,31 @@
+/*
+ * Copyright 2013 the original author or authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.gradle.buildsetup.plugins.internal.maven
+
+import spock.lang.Specification
+
+class MavenProjectXmlWriterTest extends Specification {
+
+    def writer = new MavenProjectXmlWriter()
+
+    def "removes xml element"() {
+        expect:
+        writer.prepareXml('<?xml encoding="UTF-8"?><project/>') == "<project/>"
+        writer.prepareXml('<?xml version="1.0" encoding="UTF-8"?><project/>') == "<project/>"
+        writer.prepareXml('<?xml  version="1.0"  encoding="UTF-8"  ?><project/>') == "<project/>"
+    }
+}
diff --git a/subprojects/build-setup/src/test/groovy/org/gradle/buildsetup/plugins/internal/maven/MavenProjectsCreatorSpec.groovy b/subprojects/build-setup/src/test/groovy/org/gradle/buildsetup/plugins/internal/maven/MavenProjectsCreatorSpec.groovy
new file mode 100644
index 0000000..412d090
--- /dev/null
+++ b/subprojects/build-setup/src/test/groovy/org/gradle/buildsetup/plugins/internal/maven/MavenProjectsCreatorSpec.groovy
@@ -0,0 +1,117 @@
+/*
+ * Copyright 2012 the original author or authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.gradle.buildsetup.plugins.internal.maven
+
+import org.gradle.api.internal.artifacts.mvnsettings.DefaultMavenSettingsProvider
+import org.gradle.api.internal.artifacts.mvnsettings.MavenFileLocations
+import org.gradle.test.fixtures.file.TestNameTestDirectoryProvider
+import org.junit.Rule
+import spock.lang.Specification
+
+class MavenProjectsCreatorSpec extends Specification {
+
+    @Rule TestNameTestDirectoryProvider temp
+    private settings = new DefaultMavenSettingsProvider({} as MavenFileLocations)
+    private creator = new MavenProjectsCreator()
+
+    def "creates single module project"() {
+        given:
+        def pom = temp.file("pom.xml")
+        pom.text = """<project>
+  <modelVersion>4.0.0</modelVersion>
+  <groupId>util</groupId>
+  <artifactId>util</artifactId>
+  <version>2.5</version>
+  <packaging>jar</packaging>
+</project>"""
+
+        when:
+        def mavenProjects = creator.create(settings.buildSettings(), pom) as List
+
+        then:
+        mavenProjects.size() == 1
+        mavenProjects[0].name == 'util'
+    }
+
+    def "creates multi module project"() {
+        given:
+        def parentPom = temp.file("pom.xml")
+        parentPom.text = """<project>
+  <modelVersion>4.0.0</modelVersion>
+  <groupId>org.gradle.webinar</groupId>
+  <artifactId>webinar-parent</artifactId>
+  <version>1.0-SNAPSHOT</version>
+  <packaging>pom</packaging>
+
+  <modules>
+    <module>webinar-api</module>
+  </modules>
+</project>
+"""
+
+        temp.createDir("webinar-api")
+        temp.file("webinar-api/pom.xml").text = """<project>
+  <modelVersion>4.0.0</modelVersion>
+  <artifactId>webinar-api</artifactId>
+  <packaging>jar</packaging>
+
+  <parent>
+    <groupId>org.gradle.webinar</groupId>
+    <artifactId>webinar-parent</artifactId>
+    <version>1.0-SNAPSHOT</version>
+  </parent>
+</project>
+"""
+
+        when:
+        def mavenProjects = creator.create(settings.buildSettings(), parentPom) as List
+
+        then:
+        mavenProjects.size() == 2
+        mavenProjects[0].name == 'webinar-parent'
+        mavenProjects[1].name == 'webinar-api'
+    }
+
+    def "fails with decent exception if pom is incorrect"() {
+        given:
+        def pom = temp.file("pom.xml")
+        pom.text = """<project>
+  <modelVersion>4.0.0</modelVersion>
+  <artifactId>util</artifactId>
+  <version>2.5</version>
+  <packaging>jar</packaging>
+</project>"""
+
+        when:
+        creator.create(settings.buildSettings(), pom) as List
+
+        then:
+        def ex = thrown(MavenConversionException)
+        ex.message == "Unable to create Maven project model using POM $pom."
+    }
+
+    def "fails with decent exception if pom does not exist"() {
+        def pom = temp.file("pom.xml")
+
+        when:
+        creator.create(settings.buildSettings(), pom) as List
+
+        then:
+        def ex = thrown(MavenConversionException)
+        ex.message == "Unable to create Maven project model. The POM file $pom does not exist."
+    }
+}
diff --git a/subprojects/build-setup/src/test/groovy/org/gradle/buildsetup/tasks/SetupBuildSpec.groovy b/subprojects/build-setup/src/test/groovy/org/gradle/buildsetup/tasks/SetupBuildSpec.groovy
new file mode 100644
index 0000000..2adbde9
--- /dev/null
+++ b/subprojects/build-setup/src/test/groovy/org/gradle/buildsetup/tasks/SetupBuildSpec.groovy
@@ -0,0 +1,69 @@
+/*
+ * Copyright 2012 the original author or authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.gradle.buildsetup.tasks
+
+import org.gradle.api.GradleException
+import org.gradle.buildsetup.plugins.internal.BuildSetupTypeIds
+import org.gradle.buildsetup.plugins.internal.ProjectLayoutSetupRegistry
+import org.gradle.buildsetup.plugins.internal.ProjectSetupDescriptor
+import org.gradle.util.TestUtil
+import spock.lang.Specification
+
+class SetupBuildSpec extends Specification {
+
+    SetupBuild setupBuild;
+
+    ProjectLayoutSetupRegistry projectLayoutRegistry;
+
+    ProjectSetupDescriptor projectSetupDescriptor1
+    ProjectSetupDescriptor projectSetupDescriptor2
+    ProjectSetupDescriptor projectSetupDescriptor3
+
+    def setup() {
+        setupBuild = TestUtil.builder().build().tasks.create("setupBuild", SetupBuild)
+        projectLayoutRegistry = Mock()
+        projectSetupDescriptor1 = Mock()
+        projectSetupDescriptor2 = Mock()
+        projectSetupDescriptor3 = Mock()
+        _ * projectSetupDescriptor2.id >> "supported-type"
+        _ * projectSetupDescriptor3.id >> "another-supported-type"
+        setupBuild.projectLayoutRegistry = projectLayoutRegistry
+    }
+
+    def "throws GradleException if requested setupDescriptor not supported"() {
+        setup:
+        _ * projectLayoutRegistry.get("aType") >> null
+        _ * projectLayoutRegistry.all >> [projectSetupDescriptor2, projectSetupDescriptor3]
+        when:
+        setupBuild.type = "aType"
+        setupBuild.setupProjectLayout()
+        then:
+        def e = thrown(GradleException)
+        e.message == "The requested build setup type 'aType' is not supported. Supported types: 'supported-type', 'another-supported-type'."
+
+    }
+
+    def "delegates task action to referenced setupDescriptor"() {
+        setup:
+        1 * projectLayoutRegistry.supports(BuildSetupTypeIds.BASIC) >> true
+        1 * projectLayoutRegistry.get(BuildSetupTypeIds.BASIC) >> projectSetupDescriptor1
+        when:
+        setupBuild.setupProjectLayout()
+        then:
+        1 * projectSetupDescriptor1.generateProject()
+    }
+}
diff --git a/subprojects/cli/cli.gradle b/subprojects/cli/cli.gradle
index 68118d9..46b1219 100644
--- a/subprojects/cli/cli.gradle
+++ b/subprojects/cli/cli.gradle
@@ -21,7 +21,7 @@
     It has no dependencies, and should never have any.
 */
 dependencies {
-    groovy libraries.groovy
+    testCompile libraries.groovy
 }
 
 useClassycle()
\ No newline at end of file
diff --git a/subprojects/cli/src/main/java/org/gradle/cli/CommandLineArgumentException.java b/subprojects/cli/src/main/java/org/gradle/cli/CommandLineArgumentException.java
index 82a5ff3..b34d668 100644
--- a/subprojects/cli/src/main/java/org/gradle/cli/CommandLineArgumentException.java
+++ b/subprojects/cli/src/main/java/org/gradle/cli/CommandLineArgumentException.java
@@ -17,8 +17,6 @@ package org.gradle.cli;
 
 /**
  * A {@code CommandLineArgumentException} is thrown when command-line arguments cannot be parsed.
- * 
- * @author Hans Dockter
  */
 public class CommandLineArgumentException extends RuntimeException {
     public CommandLineArgumentException(String message) {
diff --git a/subprojects/cli/src/main/java/org/gradle/cli/CommandLineConverter.java b/subprojects/cli/src/main/java/org/gradle/cli/CommandLineConverter.java
index bc9bac4..bfc2627 100644
--- a/subprojects/cli/src/main/java/org/gradle/cli/CommandLineConverter.java
+++ b/subprojects/cli/src/main/java/org/gradle/cli/CommandLineConverter.java
@@ -15,9 +15,6 @@
  */
 package org.gradle.cli;
 
-/**
- * @author Hans Dockter
- */
 public interface CommandLineConverter<T> {
     T convert(Iterable<String> args) throws CommandLineArgumentException;
 
diff --git a/subprojects/cli/src/main/java/org/gradle/cli/CommandLineOption.java b/subprojects/cli/src/main/java/org/gradle/cli/CommandLineOption.java
index 8520081..96d36c8 100644
--- a/subprojects/cli/src/main/java/org/gradle/cli/CommandLineOption.java
+++ b/subprojects/cli/src/main/java/org/gradle/cli/CommandLineOption.java
@@ -23,9 +23,9 @@ public class CommandLineOption {
     private final Set<String> options = new HashSet<String>();
     private Class<?> argumentType = Void.TYPE;
     private String description;
-    private String subcommand;
     private String deprecationWarning;
     private boolean incubating;
+    private final Set<CommandLineOption> groupWith = new HashSet<CommandLineOption>();
 
     public CommandLineOption(Iterable<String> options) {
         for (String option : options) {
@@ -47,15 +47,6 @@ public class CommandLineOption {
         return this;
     }
 
-    public String getSubcommand() {
-        return subcommand;
-    }
-
-    public CommandLineOption mapsToSubcommand(String command) {
-        this.subcommand = command;
-        return this;
-    }
-
     public String getDescription() {
         StringBuilder result = new StringBuilder();
         if (description != null) {
@@ -104,4 +95,13 @@ public class CommandLineOption {
     public String getDeprecationWarning() {
         return deprecationWarning;
     }
+
+    Set<CommandLineOption> getGroupWith() {
+        return groupWith;
+    }
+
+    void groupWith(Set<CommandLineOption> options) {
+        this.groupWith.addAll(options);
+        this.groupWith.remove(this);
+    }
 }
diff --git a/subprojects/cli/src/main/java/org/gradle/cli/CommandLineParser.java b/subprojects/cli/src/main/java/org/gradle/cli/CommandLineParser.java
index c238506..20ed20d 100644
--- a/subprojects/cli/src/main/java/org/gradle/cli/CommandLineParser.java
+++ b/subprojects/cli/src/main/java/org/gradle/cli/CommandLineParser.java
@@ -156,6 +156,21 @@ public class CommandLineParser {
     }
 
     /**
+     * Specifies that the given set of options are mutually-exclusive. Only one of the given options will be selected.
+     * The parser ignores all but the last of these options.
+     */
+    public CommandLineParser allowOneOf(String... options) {
+        Set<CommandLineOption> commandLineOptions = new HashSet<CommandLineOption>();
+        for (String option : options) {
+            commandLineOptions.add(optionsByString.get(option));
+        }
+        for (CommandLineOption commandLineOption : commandLineOptions) {
+            commandLineOption.groupWith(commandLineOptions);
+        }
+        return this;
+    }
+
+    /**
      * Prints a usage message to the given stream.
      *
      * @param out The output stream to write to.
@@ -234,7 +249,7 @@ public class CommandLineParser {
         }
         CommandLineOption option = new CommandLineOption(Arrays.asList(options));
         for (String optionStr : option.getOptions()) {
-            this.optionsByString.put(optionStr, option);
+            optionsByString.put(optionStr, option);
         }
         return option;
     }
@@ -444,8 +459,9 @@ public class CommandLineParser {
             if (option.getDeprecationWarning() != null) {
                 deprecationPrinter.println("The " + optionString + " option is deprecated - " + option.getDeprecationWarning());
             }
-            if (option.getSubcommand() != null) {
-                return state.onNonOption(option.getSubcommand());
+
+            for (CommandLineOption otherOption : option.getGroupWith()) {
+                commandLine.removeOption(otherOption);
             }
 
             return state;
diff --git a/subprojects/cli/src/main/java/org/gradle/cli/ParsedCommandLine.java b/subprojects/cli/src/main/java/org/gradle/cli/ParsedCommandLine.java
index 8179d31..3134649 100644
--- a/subprojects/cli/src/main/java/org/gradle/cli/ParsedCommandLine.java
+++ b/subprojects/cli/src/main/java/org/gradle/cli/ParsedCommandLine.java
@@ -104,4 +104,8 @@ public class ParsedCommandLine {
         presentOptions.addAll(option.getOptions());
         return parsedOption;
     }
+
+    void removeOption(CommandLineOption option) {
+        presentOptions.removeAll(option.getOptions());
+    }
 }
diff --git a/subprojects/cli/src/test/groovy/org/gradle/cli/CommandLineParserTest.groovy b/subprojects/cli/src/test/groovy/org/gradle/cli/CommandLineParserTest.groovy
index d44dbac..2859eaa 100644
--- a/subprojects/cli/src/test/groovy/org/gradle/cli/CommandLineParserTest.groovy
+++ b/subprojects/cli/src/test/groovy/org/gradle/cli/CommandLineParserTest.groovy
@@ -265,51 +265,49 @@ class CommandLineParserTest extends Specification {
         result.extraArguments == ['a', '--option', 'b']
     }
 
-    def canMapOptionToSubcommand() {
-        parser.option('a').mapsToSubcommand('subcmd')
-
-        expect:
-        def result = parser.parse(['-a', '--option', 'b'])
-        result.extraArguments == ['subcmd', '--option', 'b']
-        result.hasOption('a')
-    }
-
     def canCombineSubcommandShortOptionWithOtherShortOptions() {
-        parser.option('a').mapsToSubcommand('subcmd')
         parser.option('b')
+        parser.allowMixedSubcommandsAndOptions()
 
         when:
-        def result = parser.parse(['-abc', '--option', 'b'])
+        def result = parser.parse(['cmd', '-b', '-a'])
 
         then:
-        result.extraArguments == ['subcmd', '-b', '-c', '--option', 'b']
-        result.hasOption('a')
-        !result.hasOption('b')
+        result.extraArguments == ['cmd', '-a']
+        result.hasOption('b')
 
         when:
-        result = parser.parse(['-bac', '--option', 'b'])
+        result = parser.parse(['cmd', '-ba'])
 
         then:
-        result.extraArguments == ['subcmd', '-c', '--option', 'b']
-        result.hasOption('a')
+        result.extraArguments == ['cmd', '-a']
         result.hasOption('b')
+    }
+
+    def returnsLastMutuallyExclusiveOptionThatIsPresent() {
+        parser.option("a")
+        parser.option("b")
+        parser.option("c", "long-option")
+        parser.option("d")
+        parser.allowOneOf("a", "b", "c")
 
         when:
-        parser.allowMixedSubcommandsAndOptions()
-        result = parser.parse(['-abc', '--option', 'b'])
+        def result = parser.parse(['-a', '-b', '-c'])
 
         then:
-        result.extraArguments == ['subcmd', '-c', '--option', 'b']
-        result.hasOption('a')
-        result.hasOption('b')
+        !result.hasOption('a')
+        !result.hasOption('b')
+        result.hasOption('c')
+        result.hasOption('long-option')
 
         when:
-        result = parser.parse(['-bac', '--option', 'b'])
+        result = parser.parse(['-a', '-b', '--long-option'])
 
         then:
-        result.extraArguments == ['subcmd', '-c', '--option', 'b']
-        result.hasOption('a')
-        result.hasOption('b')
+        !result.hasOption('a')
+        !result.hasOption('b')
+        result.hasOption('c')
+        result.hasOption('long-option')
     }
 
     def singleDashIsNotConsideredAnOption() {
diff --git a/subprojects/cli/src/test/groovy/org/gradle/cli/ParsedCommandLineOptionSpec.groovy b/subprojects/cli/src/test/groovy/org/gradle/cli/ParsedCommandLineOptionSpec.groovy
index 224a638..bb0bba1 100644
--- a/subprojects/cli/src/test/groovy/org/gradle/cli/ParsedCommandLineOptionSpec.groovy
+++ b/subprojects/cli/src/test/groovy/org/gradle/cli/ParsedCommandLineOptionSpec.groovy
@@ -18,9 +18,6 @@ package org.gradle.cli
 
 import spock.lang.Specification
 
-/**
- * by Szczepan Faber, created at: 9/5/12
- */
 class ParsedCommandLineOptionSpec extends Specification {
 
     final option = new ParsedCommandLineOption();
diff --git a/subprojects/cli/src/test/groovy/org/gradle/cli/ParsedCommandLineTest.groovy b/subprojects/cli/src/test/groovy/org/gradle/cli/ParsedCommandLineTest.groovy
index 1e3f52e..22851fe 100644
--- a/subprojects/cli/src/test/groovy/org/gradle/cli/ParsedCommandLineTest.groovy
+++ b/subprojects/cli/src/test/groovy/org/gradle/cli/ParsedCommandLineTest.groovy
@@ -18,9 +18,6 @@ package org.gradle.cli
 
 import spock.lang.Specification
 
-/**
- * by Szczepan Faber, created at: 4/16/12
- */
 class ParsedCommandLineTest extends Specification {
 
     def "knows if contains an option"() {
diff --git a/subprojects/code-quality/code-quality.gradle b/subprojects/code-quality/code-quality.gradle
index 90640da..0ecaff2 100644
--- a/subprojects/code-quality/code-quality.gradle
+++ b/subprojects/code-quality/code-quality.gradle
@@ -16,7 +16,7 @@
 apply from: "$rootDir/gradle/providedConfiguration.gradle"
 
 dependencies {
-    groovy libraries.groovy
+    compile libraries.groovy
 
     compile project(':core')
     compile project(':plugins')
diff --git a/subprojects/code-quality/src/integTest/groovy/org/gradle/api/plugins/quality/CheckstylePluginIntegrationTest.groovy b/subprojects/code-quality/src/integTest/groovy/org/gradle/api/plugins/quality/CheckstylePluginIntegrationTest.groovy
index 2d8d517..8d7eb85 100644
--- a/subprojects/code-quality/src/integTest/groovy/org/gradle/api/plugins/quality/CheckstylePluginIntegrationTest.groovy
+++ b/subprojects/code-quality/src/integTest/groovy/org/gradle/api/plugins/quality/CheckstylePluginIntegrationTest.groovy
@@ -49,7 +49,7 @@ class CheckstylePluginIntegrationTest extends WellBehavedPluginTest {
 
         expect:
         fails("check")
-        failure.assertHasDescription("Execution failed for task ':checkstyleMain'")
+        failure.assertHasDescription("Execution failed for task ':checkstyleMain'.")
         failure.assertThatCause(startsWith("Checkstyle rule violations were found. See the report at:"))
         failure.error.contains("Name 'class1' must match pattern")
         file("build/reports/checkstyle/main.xml").assertContents(containsClass("org.gradle.class1"))
@@ -65,7 +65,7 @@ class CheckstylePluginIntegrationTest extends WellBehavedPluginTest {
 
         then:
         fails("check")
-        failure.assertHasDescription("Execution failed for task ':checkstyleMain'")
+        failure.assertHasDescription("Execution failed for task ':checkstyleMain'.")
         failure.assertThatCause(startsWith("Checkstyle rule violations were found. See the report at:"))
         !failure.error.contains("Name 'class1' must match pattern")
         file("build/reports/checkstyle/main.xml").assertContents(containsClass("org.gradle.class1"))
@@ -144,7 +144,7 @@ repositories {
 }
 
 dependencies {
-    groovy localGroovy()
+    compile localGroovy()
 }
         """
     }
diff --git a/subprojects/code-quality/src/integTest/groovy/org/gradle/api/plugins/quality/CodeNarcPluginIntegrationTest.groovy b/subprojects/code-quality/src/integTest/groovy/org/gradle/api/plugins/quality/CodeNarcPluginIntegrationTest.groovy
index 162f9d9..b93799d 100644
--- a/subprojects/code-quality/src/integTest/groovy/org/gradle/api/plugins/quality/CodeNarcPluginIntegrationTest.groovy
+++ b/subprojects/code-quality/src/integTest/groovy/org/gradle/api/plugins/quality/CodeNarcPluginIntegrationTest.groovy
@@ -78,7 +78,7 @@ class CodeNarcPluginIntegrationTest extends WellBehavedPluginTest {
 
         expect:
         fails("check")
-        failure.assertHasDescription("Execution failed for task ':codenarcTest'")
+        failure.assertHasDescription("Execution failed for task ':codenarcTest'.")
         failure.assertThatCause(startsWith("CodeNarc rule violations were found. See the report at:"))
         !file("build/reports/codenarc/main.html").text.contains("Class2")
         file("build/reports/codenarc/test.html").text.contains("testclass2")
@@ -100,6 +100,20 @@ class CodeNarcPluginIntegrationTest extends WellBehavedPluginTest {
 
     }
 
+    def "can configure max violations"() {
+        badCode()
+        buildFile << """
+            codenarcTest {
+                maxPriority2Violations = 1
+            }
+        """
+
+        expect:
+        succeeds("check")
+        !output.contains("CodeNarc rule violations were found. See the report at:")
+        file("build/reports/codenarc/test.html").text.contains("testclass2")
+    }
+
     private goodCode() {
         file("src/main/groovy/org/gradle/class1.java") << "package org.gradle; class class1 { }"
         file("src/test/groovy/org/gradle/testclass1.java") << "package org.gradle; class testclass1 { }"
@@ -124,7 +138,7 @@ repositories {
 }
 
 dependencies { 
-    groovy localGroovy()
+    compile localGroovy()
 }
         """
     }
diff --git a/subprojects/code-quality/src/integTest/groovy/org/gradle/api/plugins/quality/CodeQualityPluginIntegrationTest.groovy b/subprojects/code-quality/src/integTest/groovy/org/gradle/api/plugins/quality/CodeQualityPluginIntegrationTest.groovy
index 3ef3675..265fd0f 100644
--- a/subprojects/code-quality/src/integTest/groovy/org/gradle/api/plugins/quality/CodeQualityPluginIntegrationTest.groovy
+++ b/subprojects/code-quality/src/integTest/groovy/org/gradle/api/plugins/quality/CodeQualityPluginIntegrationTest.groovy
@@ -37,7 +37,7 @@ apply plugin: 'groovy'
 apply plugin: 'java'
 apply plugin: 'code-quality'
 repositories { mavenCentral() }
-dependencies { groovy localGroovy() }
+dependencies { compile localGroovy() }
 '''
         inTestDirectory().withTasks('check').run()
     }
@@ -66,7 +66,7 @@ repositories { mavenCentral() }
 apply plugin: 'groovy'
 apply plugin: 'code-quality'
 repositories { mavenCentral() }
-dependencies { groovy localGroovy() }
+dependencies { compile localGroovy() }
 '''
         writeCheckstyleConfig()
 
@@ -89,7 +89,7 @@ dependencies { groovy localGroovy() }
 apply plugin: 'groovy'
 apply plugin: 'code-quality'
 repositories { mavenCentral() }
-dependencies { groovy localGroovy() }
+dependencies { compile localGroovy() }
 '''
         writeCheckstyleConfig()
 
@@ -109,7 +109,7 @@ dependencies { groovy localGroovy() }
 apply plugin: 'groovy'
 apply plugin: 'code-quality'
 repositories { mavenCentral() }
-dependencies { groovy localGroovy() }
+dependencies { compile localGroovy() }
 '''
         writeCheckstyleConfig()
 
@@ -117,7 +117,7 @@ dependencies { groovy localGroovy() }
         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.assertHasDescription('Execution failed for task \':checkstyleMain\'.')
         failure.assertThatCause(startsWith('Checkstyle rule violations were found. See the report at'))
 
         testFile('build/checkstyle/main.xml').assertExists()
@@ -129,7 +129,7 @@ dependencies { groovy localGroovy() }
 apply plugin: 'groovy'
 apply plugin: 'code-quality'
 repositories { mavenCentral() }
-dependencies { groovy localGroovy() }
+dependencies { compile localGroovy() }
 '''
         writeCodeNarcConfigFile()
 
@@ -148,7 +148,7 @@ dependencies { groovy localGroovy() }
 apply plugin: 'groovy'
 apply plugin: 'code-quality'
 repositories { mavenCentral() }
-dependencies { groovy localGroovy() }
+dependencies { compile localGroovy() }
 '''
 
         writeCodeNarcConfigFile()
@@ -167,7 +167,7 @@ dependencies { groovy localGroovy() }
 apply plugin: 'groovy'
 apply plugin: 'code-quality'
 repositories { mavenCentral() }
-dependencies { groovy localGroovy() }
+dependencies { compile localGroovy() }
 '''
 
         writeCodeNarcConfigFile()
@@ -175,7 +175,7 @@ dependencies { groovy localGroovy() }
         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.assertHasDescription('Execution failed for task \':codenarcMain\'.')
         failure.assertThatCause(startsWith('CodeNarc rule violations were found. See the report at:'))
 
         testFile('build/reports/codenarc/main.html').assertExists()
diff --git a/subprojects/code-quality/src/integTest/groovy/org/gradle/api/plugins/quality/FindBugsPluginIntegrationTest.groovy b/subprojects/code-quality/src/integTest/groovy/org/gradle/api/plugins/quality/FindBugsPluginIntegrationTest.groovy
index 913f3a5..238fea7 100644
--- a/subprojects/code-quality/src/integTest/groovy/org/gradle/api/plugins/quality/FindBugsPluginIntegrationTest.groovy
+++ b/subprojects/code-quality/src/integTest/groovy/org/gradle/api/plugins/quality/FindBugsPluginIntegrationTest.groovy
@@ -45,7 +45,7 @@ class FindBugsPluginIntegrationTest extends WellBehavedPluginTest {
 
         expect:
         fails("check")
-        failure.assertHasDescription("Execution failed for task ':findbugsMain'")
+        failure.assertHasDescription("Execution failed for task ':findbugsMain'.")
         failure.assertThatCause(startsWith("FindBugs rule violations were found. See the report at:"))
         file("build/reports/findbugs/main.xml").assertContents(containsClass("org.gradle.BadClass"))
     }
diff --git a/subprojects/code-quality/src/integTest/groovy/org/gradle/api/plugins/quality/PmdPluginIntegrationTest.groovy b/subprojects/code-quality/src/integTest/groovy/org/gradle/api/plugins/quality/PmdPluginIntegrationTest.groovy
index dd47625..1c1f3dd 100644
--- a/subprojects/code-quality/src/integTest/groovy/org/gradle/api/plugins/quality/PmdPluginIntegrationTest.groovy
+++ b/subprojects/code-quality/src/integTest/groovy/org/gradle/api/plugins/quality/PmdPluginIntegrationTest.groovy
@@ -46,7 +46,7 @@ class PmdPluginIntegrationTest extends WellBehavedPluginTest {
 
         expect:
         fails("check")
-        failure.assertHasDescription("Execution failed for task ':pmdTest'")
+        failure.assertHasDescription("Execution failed for task ':pmdTest'.")
         failure.assertThatCause(containsString("2 PMD rule violations were found. See the report at:"))
         file("build/reports/pmd/main.xml").assertContents(not(containsClass("org.gradle.Class1")))
         file("build/reports/pmd/test.xml").assertContents(containsClass("org.gradle.Class1Test"))
@@ -112,7 +112,7 @@ class PmdPluginIntegrationTest extends WellBehavedPluginTest {
 
         expect:
         fails("pmdMain")
-        failure.assertHasDescription("Execution failed for task ':pmdMain'")
+        failure.assertHasDescription("Execution failed for task ':pmdMain'.")
         failure.assertThatCause(containsString("1 PMD rule violations were found. See the report at:"))
         file("build/reports/pmd/main.xml").assertContents(not(containsClass("org.gradle.Class1")))
         file("build/reports/pmd/main.xml").assertContents(containsClass("org.gradle.Class2"))
diff --git a/subprojects/code-quality/src/integTest/groovy/org/gradle/api/plugins/quality/PmdPluginVersionIntegrationTest.groovy b/subprojects/code-quality/src/integTest/groovy/org/gradle/api/plugins/quality/PmdPluginVersionIntegrationTest.groovy
index 5ca45d1..784bebd 100644
--- a/subprojects/code-quality/src/integTest/groovy/org/gradle/api/plugins/quality/PmdPluginVersionIntegrationTest.groovy
+++ b/subprojects/code-quality/src/integTest/groovy/org/gradle/api/plugins/quality/PmdPluginVersionIntegrationTest.groovy
@@ -23,7 +23,7 @@ import static org.gradle.util.Matchers.containsLine
 import static org.hamcrest.Matchers.containsString
 import static org.hamcrest.Matchers.not
 
- at TargetVersions(['4.3', '5.0.2'])
+ at TargetVersions(['4.3', '5.0.4'])
 class PmdPluginVersionIntegrationTest extends MultiVersionIntegrationSpec {
     def "can use different PMD versions"() {
         given:
@@ -43,7 +43,7 @@ class PmdPluginVersionIntegrationTest extends MultiVersionIntegrationSpec {
 
         expect:
         fails("check")
-        failure.assertHasDescription("Execution failed for task ':pmdTest'")
+        failure.assertHasDescription("Execution failed for task ':pmdTest'.")
         failure.assertThatCause(containsString("2 PMD rule violations were found. See the report at:"))
         file("build/reports/pmd/main.xml").assertContents(not(containsClass("org.gradle.Class1")))
         file("build/reports/pmd/test.xml").assertContents(containsClass("org.gradle.Class1Test"))
diff --git a/subprojects/code-quality/src/main/groovy/org/gradle/api/plugins/quality/CheckstyleReports.java b/subprojects/code-quality/src/main/groovy/org/gradle/api/plugins/quality/CheckstyleReports.java
index 2c8307a..d4d493a 100644
--- a/subprojects/code-quality/src/main/groovy/org/gradle/api/plugins/quality/CheckstyleReports.java
+++ b/subprojects/code-quality/src/main/groovy/org/gradle/api/plugins/quality/CheckstyleReports.java
@@ -20,15 +20,14 @@ import org.gradle.api.reporting.ReportContainer;
 import org.gradle.api.reporting.SingleFileReport;
 
 /**
- * The reporting configuration for the the {@link Checkstyle} test.
+ * The reporting configuration for the {@link Checkstyle} test.
  */
 public interface CheckstyleReports extends ReportContainer<SingleFileReport> {
 
     /**
-     * The checkstyle xml report
+     * The checkstyle XML report
      *
-     * @return The checkstyle xml report
+     * @return The checkstyle XML report
      */
     SingleFileReport getXml();
-
 }
diff --git a/subprojects/code-quality/src/main/groovy/org/gradle/api/plugins/quality/CodeNarc.groovy b/subprojects/code-quality/src/main/groovy/org/gradle/api/plugins/quality/CodeNarc.groovy
index 4c95f83..60d26e0 100644
--- a/subprojects/code-quality/src/main/groovy/org/gradle/api/plugins/quality/CodeNarc.groovy
+++ b/subprojects/code-quality/src/main/groovy/org/gradle/api/plugins/quality/CodeNarc.groovy
@@ -46,6 +46,24 @@ class CodeNarc extends SourceTask implements VerificationTask, Reporting<CodeNar
     File configFile
 
     /**
+     * The maximum number of priority 1 violations allowed before failing the build.
+     */
+    @Input
+    int maxPriority1Violations
+
+    /**
+     * The maximum number of priority 2 violations allowed before failing the build.
+     */
+    @Input
+    int maxPriority2Violations
+
+    /**
+     * The maximum number of priority 3 violations allowed before failing the build.
+     */
+    @Input
+    int maxPriority3Violations
+
+    /**
      * The format type of the CodeNarc report.
      *
      * @deprecated Use {@code reports.<report-type>.enabled} instead.
@@ -109,7 +127,7 @@ class CodeNarc extends SourceTask implements VerificationTask, Reporting<CodeNar
         antBuilder.withClasspath(getCodenarcClasspath()).execute {
             ant.taskdef(name: 'codenarc', classname: 'org.codenarc.ant.CodeNarcTask')
             try {
-                ant.codenarc(ruleSetFiles: "file:${getConfigFile()}", maxPriority1Violations: 0, maxPriority2Violations: 0, maxPriority3Violations: 0) {
+                ant.codenarc(ruleSetFiles: "file:${getConfigFile()}", maxPriority1Violations: getMaxPriority1Violations(), maxPriority2Violations: getMaxPriority2Violations(), maxPriority3Violations: getMaxPriority3Violations()) {
                     reports.enabled.each { Report r ->
                         report(type: r.name) {
                             option(name: 'outputFile', value: r.destination)
@@ -150,6 +168,4 @@ class CodeNarc extends SourceTask implements VerificationTask, Reporting<CodeNar
     CodeNarcReports reports(Closure closure) {
         reports.configure(closure)
     }
-
-
 }
diff --git a/subprojects/code-quality/src/main/groovy/org/gradle/api/plugins/quality/CodeNarcExtension.groovy b/subprojects/code-quality/src/main/groovy/org/gradle/api/plugins/quality/CodeNarcExtension.groovy
index 86d2e57..f0af5e5 100644
--- a/subprojects/code-quality/src/main/groovy/org/gradle/api/plugins/quality/CodeNarcExtension.groovy
+++ b/subprojects/code-quality/src/main/groovy/org/gradle/api/plugins/quality/CodeNarcExtension.groovy
@@ -24,6 +24,21 @@ class CodeNarcExtension extends CodeQualityExtension {
     File configFile
 
     /**
+     * The maximum number of priority 1 violations allowed before failing the build.
+     */
+    int maxPriority1Violations
+
+    /**
+     * The maximum number of priority 2 violations allowed before failing the build.
+     */
+    int maxPriority2Violations
+
+    /**
+     * The maximum number of priority 3 violations allowed before failing the build.
+     */
+    int maxPriority3Violations
+
+    /**
      * The format type of the CodeNarc report. One of <tt>html</tt>, <tt>xml</tt>, <tt>text</tt>, <tt>console</tt>.
      */
     String reportFormat
diff --git a/subprojects/code-quality/src/main/groovy/org/gradle/api/plugins/quality/CodeNarcPlugin.groovy b/subprojects/code-quality/src/main/groovy/org/gradle/api/plugins/quality/CodeNarcPlugin.groovy
index fc3cc73..7eeb27b 100644
--- a/subprojects/code-quality/src/main/groovy/org/gradle/api/plugins/quality/CodeNarcPlugin.groovy
+++ b/subprojects/code-quality/src/main/groovy/org/gradle/api/plugins/quality/CodeNarcPlugin.groovy
@@ -43,6 +43,9 @@ class CodeNarcPlugin extends AbstractCodeQualityPlugin<CodeNarc> {
         extension.with {
             toolVersion = "0.18"
             configFile = project.rootProject.file("config/codenarc/codenarc.xml")
+            maxPriority1Violations = 0
+            maxPriority2Violations = 0
+            maxPriority3Violations = 0
             reportFormat = "html"
         }
         return extension
@@ -61,6 +64,9 @@ class CodeNarcPlugin extends AbstractCodeQualityPlugin<CodeNarc> {
                 config
             }
             configFile = { extension.configFile }
+            maxPriority1Violations = { extension.maxPriority1Violations }
+            maxPriority2Violations = { extension.maxPriority2Violations }
+            maxPriority3Violations = { extension.maxPriority3Violations }
             ignoreFailures = { extension.ignoreFailures }
         }
 
@@ -80,6 +86,6 @@ class CodeNarcPlugin extends AbstractCodeQualityPlugin<CodeNarc> {
         task.with {
             description = "Run CodeNarc analysis for $sourceSet.name classes"
         }
-        task.setSource( sourceSet.allGroovy )
+        task.setSource(sourceSet.allGroovy)
     }
 }
diff --git a/subprojects/code-quality/src/main/groovy/org/gradle/api/plugins/quality/CodeNarcReports.java b/subprojects/code-quality/src/main/groovy/org/gradle/api/plugins/quality/CodeNarcReports.java
index 808dda9..db62ea8 100644
--- a/subprojects/code-quality/src/main/groovy/org/gradle/api/plugins/quality/CodeNarcReports.java
+++ b/subprojects/code-quality/src/main/groovy/org/gradle/api/plugins/quality/CodeNarcReports.java
@@ -20,21 +20,21 @@ import org.gradle.api.reporting.ReportContainer;
 import org.gradle.api.reporting.SingleFileReport;
 
 /**
- * The reporting configuration for the the {@link CodeNarc} test.
+ * The reporting configuration for the {@link CodeNarc} test.
  */
 public interface CodeNarcReports extends ReportContainer<SingleFileReport> {
 
     /**
-     * The codenarc xml report
+     * The codenarc XML report
      *
-     * @return The codenarc xml report
+     * @return The codenarc XML report
      */
     SingleFileReport getXml();
 
     /**
-     * The codenarc html report
+     * The codenarc HTML report
      *
-     * @return The codenarc html report
+     * @return The codenarc HTML report
      */
     SingleFileReport getHtml();
 
@@ -44,5 +44,4 @@ public interface CodeNarcReports extends ReportContainer<SingleFileReport> {
      * @return The codenarc text report
      */
     SingleFileReport getText();
-
 }
diff --git a/subprojects/code-quality/src/main/groovy/org/gradle/api/plugins/quality/FindBugsPlugin.groovy b/subprojects/code-quality/src/main/groovy/org/gradle/api/plugins/quality/FindBugsPlugin.groovy
index b38df0a..ca805f5 100644
--- a/subprojects/code-quality/src/main/groovy/org/gradle/api/plugins/quality/FindBugsPlugin.groovy
+++ b/subprojects/code-quality/src/main/groovy/org/gradle/api/plugins/quality/FindBugsPlugin.groovy
@@ -52,7 +52,7 @@ class FindBugsPlugin extends AbstractCodeQualityPlugin<FindBugs> {
     }
 
     private configureFindBugsConfigurations() {
-        project.configurations.add('findbugsPlugins').with {
+        project.configurations.create('findbugsPlugins').with {
             visible = false
             transitive = true
             description = 'The FindBugs plugins to be used for this project.'
diff --git a/subprojects/code-quality/src/main/groovy/org/gradle/api/plugins/quality/FindBugsReports.java b/subprojects/code-quality/src/main/groovy/org/gradle/api/plugins/quality/FindBugsReports.java
index 6c0a28c..5e213c1 100644
--- a/subprojects/code-quality/src/main/groovy/org/gradle/api/plugins/quality/FindBugsReports.java
+++ b/subprojects/code-quality/src/main/groovy/org/gradle/api/plugins/quality/FindBugsReports.java
@@ -20,25 +20,24 @@ import org.gradle.api.reporting.ReportContainer;
 import org.gradle.api.reporting.SingleFileReport;
 
 /**
- * The reporting configuration for the the {@link FindBugs} task.
+ * The reporting configuration for the {@link FindBugs} task.
  *
- * Only one of the xml or html reports can be enabled when the task executes. If more than one is enabled, an {@link org.gradle.api.InvalidUserDataException}
+ * Only one of the XML or HTML reports can be enabled when the task executes. If more than one is enabled, an {@link org.gradle.api.InvalidUserDataException}
  * will be thrown.
  */
 public interface FindBugsReports extends ReportContainer<SingleFileReport> {
 
     /**
-     * The findbugs xml report
+     * The findbugs XML report
      *
-     * @return The findbugs xml report
+     * @return The findbugs XML report
      */
     SingleFileReport getXml();
 
     /**
-     * The findbugs html report
+     * The findbugs HTML report
      *
-     * @return The findbugs html report
+     * @return The findbugs HTML report
      */
     SingleFileReport getHtml();
-    
 }
diff --git a/subprojects/code-quality/src/main/groovy/org/gradle/api/plugins/quality/JDepend.groovy b/subprojects/code-quality/src/main/groovy/org/gradle/api/plugins/quality/JDepend.groovy
index e20f8a7..b493c44 100644
--- a/subprojects/code-quality/src/main/groovy/org/gradle/api/plugins/quality/JDepend.groovy
+++ b/subprojects/code-quality/src/main/groovy/org/gradle/api/plugins/quality/JDepend.groovy
@@ -42,7 +42,7 @@ class JDepend extends DefaultTask implements Reporting<JDependReports> {
     @InputDirectory
     File classesDir
 
-    // workaround for GRADLE-2020
+    // workaround for GRADLE-2026
     /**
      * Returns the directory containing the classes to be analyzed.
      */
diff --git a/subprojects/code-quality/src/main/groovy/org/gradle/api/plugins/quality/JDependReports.java b/subprojects/code-quality/src/main/groovy/org/gradle/api/plugins/quality/JDependReports.java
index e6d038d..a40c6f6 100644
--- a/subprojects/code-quality/src/main/groovy/org/gradle/api/plugins/quality/JDependReports.java
+++ b/subprojects/code-quality/src/main/groovy/org/gradle/api/plugins/quality/JDependReports.java
@@ -20,17 +20,17 @@ import org.gradle.api.reporting.ReportContainer;
 import org.gradle.api.reporting.SingleFileReport;
 
 /**
- * The reporting configuration for the the {@link JDepend} task.
+ * The reporting configuration for the {@link JDepend} task.
  *
- * Exactly one of the xml or html reports can be enabled when the task executes. If more than one or none is enabled, an {@link org.gradle.api.InvalidUserDataException}
+ * Exactly one of the XML or HTML reports can be enabled when the task executes. If more than one or none is enabled, an {@link org.gradle.api.InvalidUserDataException}
  * will be thrown.
  */
 public interface JDependReports extends ReportContainer<SingleFileReport> {
 
     /**
-     * The jdepend xml report
+     * The jdepend XML report
      *
-     * @return The jdepend xml report
+     * @return The jdepend XML report
      */
     SingleFileReport getXml();
 
@@ -40,5 +40,4 @@ public interface JDependReports extends ReportContainer<SingleFileReport> {
      * @return The jdepend text report
      */
     SingleFileReport getText();
-     
 }
diff --git a/subprojects/code-quality/src/main/groovy/org/gradle/api/plugins/quality/PmdExtension.groovy b/subprojects/code-quality/src/main/groovy/org/gradle/api/plugins/quality/PmdExtension.groovy
index 1af0df5..d36d82e 100644
--- a/subprojects/code-quality/src/main/groovy/org/gradle/api/plugins/quality/PmdExtension.groovy
+++ b/subprojects/code-quality/src/main/groovy/org/gradle/api/plugins/quality/PmdExtension.groovy
@@ -51,6 +51,7 @@ class PmdExtension extends CodeQualityExtension {
     void setTargetJdk(def value) {
         targetJdk = TargetJdk.toVersion(value)
     }
+
     /**
      * Convenience method for adding rule sets.
      *
diff --git a/subprojects/code-quality/src/main/groovy/org/gradle/api/plugins/quality/PmdReports.java b/subprojects/code-quality/src/main/groovy/org/gradle/api/plugins/quality/PmdReports.java
index 3307f0b..fe2adda 100644
--- a/subprojects/code-quality/src/main/groovy/org/gradle/api/plugins/quality/PmdReports.java
+++ b/subprojects/code-quality/src/main/groovy/org/gradle/api/plugins/quality/PmdReports.java
@@ -20,21 +20,21 @@ import org.gradle.api.reporting.ReportContainer;
 import org.gradle.api.reporting.SingleFileReport;
 
 /**
- * The reporting configuration for the the {@link Pmd} task.
+ * The reporting configuration for the {@link Pmd} task.
  */
 public interface PmdReports extends ReportContainer<SingleFileReport> {
 
     /**
-     * The pmd (single file) html report
+     * The pmd (single file) HTML report
      *
-     * @return The pmd (single file) html report
+     * @return The pmd (single file) HTML report
      */
     SingleFileReport getHtml();
 
     /**
-     * The pmd (single file) xml report
+     * The pmd (single file) XML report
      *
-     * @return The pmd (single file) xml report
+     * @return The pmd (single file) XML report
      */
     SingleFileReport getXml();
 }
diff --git a/subprojects/code-quality/src/main/groovy/org/gradle/api/plugins/quality/internal/AbstractCodeQualityPlugin.groovy b/subprojects/code-quality/src/main/groovy/org/gradle/api/plugins/quality/internal/AbstractCodeQualityPlugin.groovy
index 23aef56..7a7b4f8 100644
--- a/subprojects/code-quality/src/main/groovy/org/gradle/api/plugins/quality/internal/AbstractCodeQualityPlugin.groovy
+++ b/subprojects/code-quality/src/main/groovy/org/gradle/api/plugins/quality/internal/AbstractCodeQualityPlugin.groovy
@@ -64,7 +64,7 @@ abstract class AbstractCodeQualityPlugin<T> implements Plugin<ProjectInternal> {
     }
 
     protected void createConfigurations() {
-        project.configurations.add(configurationName).with {
+        project.configurations.create(configurationName).with {
             visible = false
             transitive = true
             description = "The ${toolName} libraries to be used for this project."
@@ -109,7 +109,7 @@ abstract class AbstractCodeQualityPlugin<T> implements Plugin<ProjectInternal> {
     private void configureSourceSetRule() {
         project.plugins.withType(basePlugin) {
             project.sourceSets.all { SourceSet sourceSet ->
-                T task = project.tasks.add(sourceSet.getTaskName(taskBaseName, null), taskType)
+                T task = project.tasks.create(sourceSet.getTaskName(taskBaseName, null), taskType)
                 configureForSourceSet(sourceSet, task)
             }
         }
diff --git a/subprojects/code-quality/src/test/groovy/org/gradle/api/plugins/quality/CheckstylePluginTest.groovy b/subprojects/code-quality/src/test/groovy/org/gradle/api/plugins/quality/CheckstylePluginTest.groovy
index afadcfd..07edfff 100644
--- a/subprojects/code-quality/src/test/groovy/org/gradle/api/plugins/quality/CheckstylePluginTest.groovy
+++ b/subprojects/code-quality/src/test/groovy/org/gradle/api/plugins/quality/CheckstylePluginTest.groovy
@@ -18,7 +18,7 @@ package org.gradle.api.plugins.quality
 import org.gradle.api.Project
 import org.gradle.api.plugins.ReportingBasePlugin
 import org.gradle.api.tasks.SourceSet
-import org.gradle.util.HelperUtil
+import org.gradle.util.TestUtil
 import org.gradle.api.plugins.JavaBasePlugin
 
 import spock.lang.Specification
@@ -28,7 +28,7 @@ import static org.hamcrest.Matchers.*
 import static spock.util.matcher.HamcrestSupport.that
 
 class CheckstylePluginTest extends Specification {
-    Project project = HelperUtil.createRootProject()
+    Project project = TestUtil.createRootProject()
 
     def setup() {
         project.plugins.apply(CheckstylePlugin)
@@ -88,7 +88,7 @@ class CheckstylePluginTest extends Specification {
     }
     
     def "configures any additional checkstyle tasks"() {
-        def task = project.tasks.add("checkstyleCustom", Checkstyle)
+        def task = project.tasks.create("checkstyleCustom", Checkstyle)
 
         expect:
         task.description == null
@@ -153,7 +153,7 @@ class CheckstylePluginTest extends Specification {
     }
     
     def "can customize any additional checkstyle tasks via extension"() {
-        def task = project.tasks.add("checkstyleCustom", Checkstyle)
+        def task = project.tasks.create("checkstyleCustom", Checkstyle)
         project.checkstyle {
             configFile = project.file("checkstyle-config")
             configProperties = [foo: "foo"]
diff --git a/subprojects/code-quality/src/test/groovy/org/gradle/api/plugins/quality/CheckstyleTest.groovy b/subprojects/code-quality/src/test/groovy/org/gradle/api/plugins/quality/CheckstyleTest.groovy
index 6b72fd4..c53f5e8 100644
--- a/subprojects/code-quality/src/test/groovy/org/gradle/api/plugins/quality/CheckstyleTest.groovy
+++ b/subprojects/code-quality/src/test/groovy/org/gradle/api/plugins/quality/CheckstyleTest.groovy
@@ -23,7 +23,7 @@ import spock.lang.Specification
 class CheckstyleTest extends Specification {
     def "default configuration"() {
         def project = ProjectBuilder.builder().build()
-        def checkstyle = project.tasks.add("checkstyle", Checkstyle)
+        def checkstyle = project.tasks.create("checkstyle", Checkstyle)
 
         expect:
         with(checkstyle) {
diff --git a/subprojects/code-quality/src/test/groovy/org/gradle/api/plugins/quality/CodeNarcPluginTest.groovy b/subprojects/code-quality/src/test/groovy/org/gradle/api/plugins/quality/CodeNarcPluginTest.groovy
index 17ac7b4..134958b 100644
--- a/subprojects/code-quality/src/test/groovy/org/gradle/api/plugins/quality/CodeNarcPluginTest.groovy
+++ b/subprojects/code-quality/src/test/groovy/org/gradle/api/plugins/quality/CodeNarcPluginTest.groovy
@@ -18,7 +18,7 @@ package org.gradle.api.plugins.quality
 import org.gradle.api.Project
 import org.gradle.api.plugins.GroovyBasePlugin
 import org.gradle.api.tasks.SourceSet
-import org.gradle.util.HelperUtil
+import org.gradle.util.TestUtil
 import spock.lang.Specification
 import static org.gradle.util.Matchers.dependsOn
 import static org.hamcrest.Matchers.hasItems
@@ -26,7 +26,7 @@ import static spock.util.matcher.HamcrestSupport.that
 import org.gradle.api.plugins.ReportingBasePlugin
 
 class CodeNarcPluginTest extends Specification {
-    Project project = HelperUtil.createRootProject()
+    Project project = TestUtil.createRootProject()
 
     def setup() {
         project.plugins.apply(CodeNarcPlugin)
@@ -51,6 +51,9 @@ class CodeNarcPluginTest extends Specification {
         expect:
         CodeNarcExtension codenarc = project.extensions.codenarc
         codenarc.configFile == project.file("config/codenarc/codenarc.xml")
+        codenarc.maxPriority1Violations == 0
+        codenarc.maxPriority2Violations == 0
+        codenarc.maxPriority3Violations == 0
         codenarc.reportFormat == "html"
         codenarc.reportsDir == project.file("build/reports/codenarc")
         codenarc.sourceSets == []
@@ -79,6 +82,9 @@ class CodeNarcPluginTest extends Specification {
             assert source as List == sourceSet.allGroovy  as List
             assert codenarcClasspath == project.configurations.codenarc
             assert configFile == project.file("config/codenarc/codenarc.xml")
+            assert maxPriority1Violations == 0
+            assert maxPriority2Violations == 0
+            assert maxPriority3Violations == 0
             assert reportFormat == "html"
             assert reportFile == project.file("build/reports/codenarc/${sourceSet.name}.html")
             assert ignoreFailures == false
@@ -96,6 +102,9 @@ class CodeNarcPluginTest extends Specification {
         project.codenarc {
             checkTasks = ["codenarcMain"]
             configFile = project.file("codenarc-config")
+            maxPriority1Violations = 10
+            maxPriority2Violations = 50
+            maxPriority3Violations = 200
             reportFormat = "xml"
             reportsDir = project.file("codenarc-reports")
             ignoreFailures = true
@@ -115,6 +124,9 @@ class CodeNarcPluginTest extends Specification {
             assert source as List == sourceSet.allGroovy as List
             assert codenarcClasspath == project.configurations.codenarc
             assert configFile == project.file("codenarc-config")
+            assert maxPriority1Violations == 10
+            assert maxPriority2Violations == 50
+            assert maxPriority3Violations == 200
             assert reportFormat == "xml"
             assert reportFile == project.file("codenarc-reports/${sourceSet.name}.xml")
             assert ignoreFailures == true
@@ -122,23 +134,29 @@ class CodeNarcPluginTest extends Specification {
     }
 
     def "configures any additional codenarc tasks"() {
-        def task = project.tasks.add("codenarcCustom", CodeNarc)
+        def task = project.tasks.create("codenarcCustom", CodeNarc)
 
         expect:
         task.description == null
         task.source.isEmpty()
         task.codenarcClasspath == project.configurations.codenarc
         task.configFile == project.file("config/codenarc/codenarc.xml")
+        task.maxPriority1Violations == 0
+        task.maxPriority2Violations == 0
+        task.maxPriority3Violations == 0
         task.reportFormat == "html"
         task.reportFile == project.file("build/reports/codenarc/custom.html")
         task.ignoreFailures == false
     }
 
     def "can customize additional tasks via extension"() {
-        def task = project.tasks.add("codenarcCustom", CodeNarc)
+        def task = project.tasks.create("codenarcCustom", CodeNarc)
 
         project.codenarc {
             configFile = project.file("codenarc-config")
+            maxPriority1Violations = 10
+            maxPriority2Violations = 50
+            maxPriority3Violations = 200
             reportFormat = "xml"
             reportsDir = project.file("codenarc-reports")
             ignoreFailures = true
@@ -149,6 +167,9 @@ class CodeNarcPluginTest extends Specification {
         task.source.isEmpty()
         task.codenarcClasspath == project.configurations.codenarc
         task.configFile == project.file("codenarc-config")
+        task.maxPriority1Violations == 10
+        task.maxPriority2Violations == 50
+        task.maxPriority3Violations == 200
         task.reportFormat == "xml"
         task.reportFile == project.file("codenarc-reports/custom.xml")
         task.ignoreFailures == true
@@ -162,7 +183,7 @@ class CodeNarcPluginTest extends Specification {
             other
         }
 
-        project.tasks.add("codenarcCustom", CodeNarc)
+        project.tasks.create("codenarcCustom", CodeNarc)
         
         expect:
         that(project.check, dependsOn(hasItems("codenarcMain", "codenarcTest", "codenarcOther")))
@@ -176,7 +197,7 @@ class CodeNarcPluginTest extends Specification {
             other
         }
 
-        project.tasks.add("codenarcCustom", CodeNarc)
+        project.tasks.create("codenarcCustom", CodeNarc)
 
         project.codenarc {
             sourceSets = [project.sourceSets.main]
@@ -187,7 +208,7 @@ class CodeNarcPluginTest extends Specification {
     }
 
     def "can customize task directly"() {
-        CodeNarc task = project.tasks.add("codenarcCustom", CodeNarc)
+        CodeNarc task = project.tasks.create("codenarcCustom", CodeNarc)
 
         task.reports.xml {
             enabled true
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 147bd99..b07f03c 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
@@ -19,7 +19,7 @@ import org.gradle.api.Project
 import org.gradle.api.plugins.GroovyPlugin
 import org.gradle.api.plugins.JavaPlugin
 import org.gradle.api.plugins.ReportingBasePlugin
-import org.gradle.util.HelperUtil
+import org.gradle.util.TestUtil
 import org.junit.Test
 import static org.gradle.util.Matchers.*
 import static org.hamcrest.Matchers.*
@@ -28,7 +28,7 @@ import org.gradle.api.plugins.JavaBasePlugin
 import org.gradle.api.plugins.GroovyBasePlugin
 
 class CodeQualityPluginTest {
-    private final Project project = HelperUtil.createRootProject()
+    private final Project project = TestUtil.createRootProject()
     private final CodeQualityPlugin plugin = new CodeQualityPlugin()
 
     @Test public void appliesCheckstyleAndCodeNarcPlugins() {
@@ -78,7 +78,7 @@ class CodeQualityPluginTest {
         assertThat(task.properties, equalTo(project.checkstyleProperties))
         assertThat(task, dependsOn(JavaPlugin.TEST_CLASSES_TASK_NAME))
 
-        project.sourceSets.add('custom')
+        project.sourceSets.create('custom')
         task = project.tasks['checkstyleCustom']
         assertThat(task, instanceOf(Checkstyle))
         assertThat(task.source as List, equalTo(project.sourceSets.custom.allJava as List))
@@ -114,7 +114,7 @@ class CodeQualityPluginTest {
         assertThat(task.reportFile, equalTo(project.file("build/reports/codenarc/test.html")))
         assertThat(task, dependsOn())
 
-        project.sourceSets.add('custom')
+        project.sourceSets.create('custom')
         task = project.tasks['codenarcCustom']
         assertThat(task, instanceOf(CodeNarc))
         assertThat(task.source as List, equalTo(project.sourceSets.custom.allGroovy as List))
@@ -135,7 +135,7 @@ class CodeQualityPluginTest {
 
         project.checkstyleProperties.someProp = 'someValue'
 
-        def task = project.tasks.add('checkstyleApi', Checkstyle)
+        def task = project.tasks.create('checkstyleApi', Checkstyle)
         assertThat(task.source, isEmpty())
         assertThat(task.checkstyleClasspath, equalTo(project.configurations.checkstyle))
         assertThat(task.configFile, equalTo(project.checkstyleConfigFile))
@@ -148,7 +148,7 @@ class CodeQualityPluginTest {
 
         project.checkstyleProperties.someProp = 'someValue'
 
-        def task = project.tasks.add('codenarcApi', CodeNarc)
+        def task = project.tasks.create('codenarcApi', CodeNarc)
         assertThat(task.source, isEmpty())
         assertThat(task.codenarcClasspath, equalTo(project.configurations.codenarc))
         assertThat(task.configFile, equalTo(project.codeNarcConfigFile))
diff --git a/subprojects/code-quality/src/test/groovy/org/gradle/api/plugins/quality/FindBugsPluginTest.groovy b/subprojects/code-quality/src/test/groovy/org/gradle/api/plugins/quality/FindBugsPluginTest.groovy
index e7640cd..05ccfe8 100644
--- a/subprojects/code-quality/src/test/groovy/org/gradle/api/plugins/quality/FindBugsPluginTest.groovy
+++ b/subprojects/code-quality/src/test/groovy/org/gradle/api/plugins/quality/FindBugsPluginTest.groovy
@@ -19,8 +19,7 @@ import org.gradle.api.Project
 import org.gradle.api.plugins.JavaBasePlugin
 import org.gradle.api.plugins.ReportingBasePlugin
 import org.gradle.api.tasks.SourceSet
-import org.gradle.util.HelperUtil
-
+import org.gradle.util.TestUtil
 import spock.lang.Specification
 
 import static org.gradle.util.Matchers.dependsOn
@@ -28,7 +27,7 @@ import static org.hamcrest.Matchers.*
 import static spock.util.matcher.HamcrestSupport.that
 
 class FindBugsPluginTest extends Specification {
-    Project project = HelperUtil.createRootProject()
+    Project project = TestUtil.createRootProject()
 
     def setup() {
         project.plugins.apply(FindBugsPlugin)
@@ -96,7 +95,7 @@ class FindBugsPluginTest extends Specification {
     }
 
     def "configures any additional FindBugs tasks"() {
-        def task = project.tasks.add("findbugsCustom", FindBugs)
+        def task = project.tasks.create("findbugsCustom", FindBugs)
 
         expect:
         with(task) {
@@ -176,7 +175,7 @@ class FindBugsPluginTest extends Specification {
     }
     
     def "can customize any additional FindBugs tasks via extension"() {
-        def task = project.tasks.add("findbugsCustom", FindBugs)
+        def task = project.tasks.create("findbugsCustom", FindBugs)
         project.findbugs {
             reportsDir = project.file("findbugs-reports")
             ignoreFailures = true
diff --git a/subprojects/code-quality/src/test/groovy/org/gradle/api/plugins/quality/FindBugsTest.groovy b/subprojects/code-quality/src/test/groovy/org/gradle/api/plugins/quality/FindBugsTest.groovy
index 1c3077e..3a9d1d9 100644
--- a/subprojects/code-quality/src/test/groovy/org/gradle/api/plugins/quality/FindBugsTest.groovy
+++ b/subprojects/code-quality/src/test/groovy/org/gradle/api/plugins/quality/FindBugsTest.groovy
@@ -27,7 +27,7 @@ class FindBugsTest extends Specification {
 
     def setup() {
         def project = ProjectBuilder.builder().build()
-        findbugs = project.tasks.add("findbugs", FindBugs)
+        findbugs = project.tasks.create("findbugs", FindBugs)
     }
 
     def "fails when errorCount greater than zero"() {
diff --git a/subprojects/code-quality/src/test/groovy/org/gradle/api/plugins/quality/JDependPluginTest.groovy b/subprojects/code-quality/src/test/groovy/org/gradle/api/plugins/quality/JDependPluginTest.groovy
index 424a8eb..62c2b0e 100644
--- a/subprojects/code-quality/src/test/groovy/org/gradle/api/plugins/quality/JDependPluginTest.groovy
+++ b/subprojects/code-quality/src/test/groovy/org/gradle/api/plugins/quality/JDependPluginTest.groovy
@@ -19,14 +19,14 @@ import org.gradle.api.Project
 import org.gradle.api.plugins.JavaBasePlugin
 import org.gradle.api.plugins.ReportingBasePlugin
 import org.gradle.api.tasks.SourceSet
-import org.gradle.util.HelperUtil
+import org.gradle.util.TestUtil
 import spock.lang.Specification
 import static org.gradle.util.Matchers.dependsOn
 import static org.hamcrest.Matchers.*
 import static spock.util.matcher.HamcrestSupport.that
 
 class JDependPluginTest extends Specification {
-    Project project = HelperUtil.createRootProject()
+    Project project = TestUtil.createRootProject()
 
     def setup() {
         project.plugins.apply(JDependPlugin)
@@ -80,7 +80,7 @@ class JDependPluginTest extends Specification {
     }
 
     def "configures any additional JDepend tasks"() {
-        def task = project.tasks.add("jdependCustom", JDepend)
+        def task = project.tasks.create("jdependCustom", JDepend)
 
         expect:
         task.description == null
@@ -124,7 +124,7 @@ class JDependPluginTest extends Specification {
     }
 
     def "can customize any additional JDepend tasks via extension"() {
-        def task = project.tasks.add("jdependCustom", JDepend)
+        def task = project.tasks.create("jdependCustom", JDepend)
         project.jdepend {
             reportsDir = project.file("jdepend-reports")
         }
diff --git a/subprojects/code-quality/src/test/groovy/org/gradle/api/plugins/quality/PmdPluginTest.groovy b/subprojects/code-quality/src/test/groovy/org/gradle/api/plugins/quality/PmdPluginTest.groovy
index b708011..6b0544e 100644
--- a/subprojects/code-quality/src/test/groovy/org/gradle/api/plugins/quality/PmdPluginTest.groovy
+++ b/subprojects/code-quality/src/test/groovy/org/gradle/api/plugins/quality/PmdPluginTest.groovy
@@ -19,7 +19,7 @@ import org.gradle.api.Project
 import org.gradle.api.plugins.JavaBasePlugin
 import org.gradle.api.plugins.ReportingBasePlugin
 import org.gradle.api.tasks.SourceSet
-import org.gradle.util.HelperUtil
+import org.gradle.util.TestUtil
 import spock.lang.Specification
 
 import static org.gradle.util.Matchers.dependsOn
@@ -27,7 +27,7 @@ import static org.hamcrest.Matchers.*
 import static spock.util.matcher.HamcrestSupport.that
 
 class PmdPluginTest extends Specification {
-    Project project = HelperUtil.createRootProject()
+    Project project = TestUtil.createRootProject()
 
     def setup() {
         project.plugins.apply(PmdPlugin)
@@ -110,7 +110,7 @@ class PmdPluginTest extends Specification {
     }
 
     def "configures any additional PMD tasks"() {
-        def task = project.tasks.add("pmdCustom", Pmd)
+        def task = project.tasks.create("pmdCustom", Pmd)
 
         expect:
         task.description == null
@@ -175,7 +175,7 @@ class PmdPluginTest extends Specification {
     }
 
     def "can customize any additional PMD tasks via extension"() {
-        def task = project.tasks.add("pmdCustom", Pmd)
+        def task = project.tasks.create("pmdCustom", Pmd)
         project.pmd {
             ruleSets = ["braces", "unusedcode"]
             ruleSetFiles = project.files("my-ruleset.xml")
diff --git a/subprojects/core-impl/core-impl.gradle b/subprojects/core-impl/core-impl.gradle
index 495307e..7962bae 100644
--- a/subprojects/core-impl/core-impl.gradle
+++ b/subprojects/core-impl/core-impl.gradle
@@ -7,8 +7,6 @@ configurations {
 }
 
 dependencies {
-    groovy libraries.groovy
-
     compile project(":core")
 
     compile libraries.commons_httpclient
@@ -26,6 +24,8 @@ dependencies {
 
     mvn3Input libraries.maven3
 
+    testCompile libraries.groovy
+
     //this dependency is necessary to run IvySFtpResolverIntegrationTest on ibm jdk
     integTestRuntime "org.bouncycastle:bcprov-jdk15:1.46 at jar"
 }
@@ -51,6 +51,7 @@ def allJarJars = tasks.withType(JarJar)
 ideaModule.dependsOn allJarJars
 eclipseClasspath.dependsOn allJarJars
 useTestFixtures()
+useTestFixtures(project: ":messaging")
 
 def avoidConflictingPlexusComponents(JarJar task) {
     //DefaultSecDispatcher component is configured in 2 different jars (META-INF/plexus/components.xml).
diff --git a/subprojects/core-impl/src/integTest/groovy/org/gradle/integtests/resolve/ArtifactDependenciesIntegrationTest.groovy b/subprojects/core-impl/src/integTest/groovy/org/gradle/integtests/resolve/ArtifactDependenciesIntegrationTest.groovy
index 89a69ea..441b8c2 100644
--- a/subprojects/core-impl/src/integTest/groovy/org/gradle/integtests/resolve/ArtifactDependenciesIntegrationTest.groovy
+++ b/subprojects/core-impl/src/integTest/groovy/org/gradle/integtests/resolve/ArtifactDependenciesIntegrationTest.groovy
@@ -165,6 +165,36 @@ project(':b') {
     }
 
     @Test
+    public void artifactFilesPreserveFixedOrder() {
+        repo.module('org', 'leaf1').publish()
+        repo.module('org', 'leaf2').publish()
+        repo.module('org', 'leaf3').publish()
+        repo.module('org', 'leaf4').publish()
+
+        repo.module('org', 'middle1').dependsOn("leaf1", "leaf2").publish()
+        repo.module('org', 'middle2').dependsOn("leaf3", "leaf4").publish()
+
+        repo.module('org', 'top').dependsOn("middle1", "middle2").publish()
+
+        testFile('build.gradle') << """
+            repositories {
+                maven { url '${repo.uri}' }
+            }
+            configurations {
+                compile
+            }
+            dependencies {
+                compile "org:middle2:1.0", "org:middle1:1.0"
+            }
+            task test << {
+                assert configurations.compile.files.collect { it.name } == ['middle2-1.0.jar', 'middle1-1.0.jar', 'leaf3-1.0.jar', 'leaf4-1.0.jar', 'leaf1-1.0.jar', 'leaf2-1.0.jar']
+            }
+        """
+
+        executer.withTasks("test").run()
+    }
+
+    @Test
     public void exposesMetaDataAboutResolvedArtifactsInAFixedOrder() {
         def module = repo.module('org.gradle.test', 'lib', '1.0')
         module.artifact(type: 'zip')
@@ -186,7 +216,7 @@ dependencies {
     compile "org.gradle.test:dist:1.0"
 }
 task test << {
-    assert configurations.compile.files.collect { it.name } == ['lib-1.0.jar', 'lib-1.0.zip', 'lib-1.0-classifier.jar', 'dist-1.0.zip']
+    assert configurations.compile.files.collect { it.name } == ['lib-1.0.jar', 'lib-1.0-classifier.jar', 'lib-1.0.zip', 'dist-1.0.zip']
     def artifacts = configurations.compile.resolvedConfiguration.resolvedArtifacts as List
     assert artifacts.size() == 4
     assert artifacts[0].name == 'lib'
@@ -611,7 +641,7 @@ task test << {
 
         failure
                 .assertHasFileName("Build file '" + buildFile.getPath() + "'")
-                .assertHasDescription("Execution failed for task ':listJars'");
+                .assertHasDescription("Execution failed for task ':listJars'.");
 
         failure.assertResolutionFailure(':compile')
                 .assertHasCause("Could not find test:unknownProjectA:1.2.")
@@ -622,7 +652,7 @@ task test << {
     public void projectCanDependOnItself() {
         TestFile buildFile = testFile("build.gradle");
         buildFile << '''
-            configurations { compile; add('default') }
+            configurations { compile; create('default') }
             dependencies { compile project(':') }
             task jar1(type: Jar) { destinationDir = buildDir; baseName = '1' }
             task jar2(type: Jar) { destinationDir = buildDir; baseName = '2' }
diff --git a/subprojects/core-impl/src/integTest/groovy/org/gradle/integtests/resolve/ClientModuleDependenciesResolveIntegrationTest.groovy b/subprojects/core-impl/src/integTest/groovy/org/gradle/integtests/resolve/ClientModuleDependenciesResolveIntegrationTest.groovy
index b347c86..5529aaf 100644
--- a/subprojects/core-impl/src/integTest/groovy/org/gradle/integtests/resolve/ClientModuleDependenciesResolveIntegrationTest.groovy
+++ b/subprojects/core-impl/src/integTest/groovy/org/gradle/integtests/resolve/ClientModuleDependenciesResolveIntegrationTest.groovy
@@ -18,9 +18,6 @@ package org.gradle.integtests.resolve
 import org.gradle.integtests.fixtures.AbstractDependencyResolutionTest
 import org.junit.Test
 
-/**
- * @author Hans Dockter
- */
 public class ClientModuleDependenciesResolveIntegrationTest extends AbstractDependencyResolutionTest {
     @Test
     public void "uses metadata from Client Module and looks up artifact in declared repositories"() {
diff --git a/subprojects/core-impl/src/integTest/groovy/org/gradle/integtests/resolve/DependencyNotationIntegrationSpec.groovy b/subprojects/core-impl/src/integTest/groovy/org/gradle/integtests/resolve/DependencyNotationIntegrationSpec.groovy
index f2c2a12..445dfb1 100644
--- a/subprojects/core-impl/src/integTest/groovy/org/gradle/integtests/resolve/DependencyNotationIntegrationSpec.groovy
+++ b/subprojects/core-impl/src/integTest/groovy/org/gradle/integtests/resolve/DependencyNotationIntegrationSpec.groovy
@@ -19,9 +19,6 @@ package org.gradle.integtests.resolve
 import org.gradle.integtests.fixtures.AbstractIntegrationSpec
 import org.hamcrest.Matchers
 
-/**
- * by Szczepan Faber, created at: 11/9/11
- */
 class DependencyNotationIntegrationSpec extends AbstractIntegrationSpec {
 
     def "understands dependency notations"() {
diff --git a/subprojects/core-impl/src/integTest/groovy/org/gradle/integtests/resolve/DependencyResolveRulesIntegrationTest.groovy b/subprojects/core-impl/src/integTest/groovy/org/gradle/integtests/resolve/DependencyResolveRulesIntegrationTest.groovy
index df3c85f..f83563f 100644
--- a/subprojects/core-impl/src/integTest/groovy/org/gradle/integtests/resolve/DependencyResolveRulesIntegrationTest.groovy
+++ b/subprojects/core-impl/src/integTest/groovy/org/gradle/integtests/resolve/DependencyResolveRulesIntegrationTest.groovy
@@ -21,9 +21,6 @@ import org.gradle.integtests.fixtures.AbstractIntegrationSpec
 
 import static org.gradle.util.TextUtil.toPlatformLineSeparators
 
-/**
- * @author Szczepan Faber, @date 03.03.11
- */
 class DependencyResolveRulesIntegrationTest extends AbstractIntegrationSpec {
 
     void "forces multiple modules by rule"()
@@ -717,6 +714,39 @@ class DependencyResolveRulesIntegrationTest extends AbstractIntegrationSpec {
         failure.assertResolutionFailure(":conf").assertHasCause("Invalid format: 'foobar'")
     }
 
+    def "substituted module version participates in conflict resolution"()
+    {
+        mavenRepo.module("org", "a", "2.0").dependsOn("org", "b", "2.0").publish()
+        mavenRepo.module("org", "b", "2.0").dependsOn("org", "c", "2.0").publish()
+        mavenRepo.module("org", "c", "2.0").publish()
+
+        buildFile << """
+            $common
+
+            dependencies {
+                conf 'org:a:1.0', 'org:a:2.0'
+            }
+
+            configurations.conf.resolutionStrategy.eachDependency {
+                if (it.requested.name == 'a' && it.requested.version == '1.0') {
+                    it.useTarget group: 'org', name: 'c', version: '1.1'
+                }
+	        }
+"""
+
+        when:
+        run("dependencies")
+
+        then:
+        output.contains(toPlatformLineSeparators("""
+conf
++--- org:a:1.0 -> org:c:2.0
+\\--- org:a:2.0
+     \\--- org:b:2.0
+          \\--- org:c:2.0
+"""))
+    }
+
     def "module selected by conflict resolution can be selected again in a another pass of conflict resolution"()
     {
         mavenRepo.module("org", "a", "1.0").publish()
diff --git a/subprojects/core-impl/src/integTest/groovy/org/gradle/integtests/resolve/DetachedConfigurationsIntegrationTest.groovy b/subprojects/core-impl/src/integTest/groovy/org/gradle/integtests/resolve/DetachedConfigurationsIntegrationTest.groovy
new file mode 100644
index 0000000..f3570f8
--- /dev/null
+++ b/subprojects/core-impl/src/integTest/groovy/org/gradle/integtests/resolve/DetachedConfigurationsIntegrationTest.groovy
@@ -0,0 +1,62 @@
+/*
+ * Copyright 2012 the original author or authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS 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.resolve
+
+import org.gradle.integtests.fixtures.AbstractIntegrationSpec
+import spock.lang.Issue
+
+class DetachedConfigurationsIntegrationTest extends AbstractIntegrationSpec {
+
+    @Issue("GRADLE-2889")
+    def "detached configurations may have separate dependencies"() {
+        settingsFile << "include 'a', 'b'"
+        mavenRepo.module("org", "foo").publish()
+        mavenRepo.module("org", "bar").publish()
+
+        buildFile << """
+            allprojects {
+                configurations {
+                    foo
+                }
+                repositories {
+                    maven { url "${mavenRepo.uri}" }
+                }
+                task checkDependencies() << {
+                    configurations.each { conf ->
+                        def declared = conf.dependencies
+                        def detached = project.configurations.detachedConfiguration(declared as Dependency[])
+                        def resolved = detached.resolvedConfiguration.getFirstLevelModuleDependencies()
+                        assert declared*.name == resolved*.moduleName
+                    }
+                }
+            }
+            project(":a") {
+                dependencies {
+                    foo "org:foo:1.0"
+                }
+            }
+            project(":b") {
+                dependencies {
+                    foo "org:bar:1.0"
+                }
+            }
+        """
+
+        expect:
+        run "checkDependencies"
+    }
+}
diff --git a/subprojects/core-impl/src/integTest/groovy/org/gradle/integtests/resolve/ExtendingConfigurationsIntegrationTest.groovy b/subprojects/core-impl/src/integTest/groovy/org/gradle/integtests/resolve/ExtendingConfigurationsIntegrationTest.groovy
new file mode 100644
index 0000000..9dd235e
--- /dev/null
+++ b/subprojects/core-impl/src/integTest/groovy/org/gradle/integtests/resolve/ExtendingConfigurationsIntegrationTest.groovy
@@ -0,0 +1,60 @@
+/*
+ * Copyright 2013 the original author or authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS 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.resolve;
+
+import org.gradle.integtests.fixtures.AbstractDependencyResolutionTest
+import spock.lang.Issue;
+
+public class ExtendingConfigurationsIntegrationTest extends AbstractDependencyResolutionTest {
+
+    @Issue("GRADLE-2873")
+    def "may replace configuration extension targets"() {
+        mavenRepo.module("org", "foo").publish()
+        mavenRepo.module("org", "bar").publish()
+
+        buildFile << """
+            configurations {
+                fooConf
+                barConf
+                conf
+            }
+
+            dependencies {
+                fooConf 'org:foo:1.0'
+                barConf 'org:bar:1.0'
+            }
+
+            task check << {
+                configurations.conf.extendsFrom(configurations.fooConf)
+                assert configurations.conf.allDependencies*.name == ['foo']
+
+                //purposefully again:
+                configurations.conf.extendsFrom(configurations.fooConf)
+                assert configurations.conf.allDependencies*.name == ['foo']
+
+                //replace:
+                configurations.conf.extendsFrom = [configurations.barConf] as Set
+                assert configurations.conf.allDependencies*.name == ['bar']
+            }
+        """
+
+        when:
+        run "check"
+
+        then:
+        noExceptionThrown()
+    }
+}
diff --git a/subprojects/core-impl/src/integTest/groovy/org/gradle/integtests/resolve/ForcedModulesIntegrationTest.groovy b/subprojects/core-impl/src/integTest/groovy/org/gradle/integtests/resolve/ForcedModulesIntegrationTest.groovy
index 94bebef..d68511d 100644
--- a/subprojects/core-impl/src/integTest/groovy/org/gradle/integtests/resolve/ForcedModulesIntegrationTest.groovy
+++ b/subprojects/core-impl/src/integTest/groovy/org/gradle/integtests/resolve/ForcedModulesIntegrationTest.groovy
@@ -17,10 +17,7 @@ package org.gradle.integtests.resolve
 
 import org.gradle.integtests.fixtures.AbstractIntegrationSpec
 
-/**
- * @author Szczepan Faber, @date 03.03.11
- */
-class ForcedModulesIntegrationTest extends AbstractIntegrationSpec {
+public class ForcedModulesIntegrationTest extends AbstractIntegrationSpec {
 
     void "can force the version of a particular module"() {
         mavenRepo.module("org", "foo", '1.3.3').publish()
diff --git a/subprojects/core-impl/src/integTest/groovy/org/gradle/integtests/resolve/ProjectDependenciesIntegrationTest.groovy b/subprojects/core-impl/src/integTest/groovy/org/gradle/integtests/resolve/ProjectDependenciesIntegrationTest.groovy
index 23e3602..7c231ce 100644
--- a/subprojects/core-impl/src/integTest/groovy/org/gradle/integtests/resolve/ProjectDependenciesIntegrationTest.groovy
+++ b/subprojects/core-impl/src/integTest/groovy/org/gradle/integtests/resolve/ProjectDependenciesIntegrationTest.groovy
@@ -21,9 +21,6 @@ package org.gradle.integtests.resolve
 import org.gradle.integtests.fixtures.AbstractDependencyResolutionTest
 import spock.lang.Issue
 
-/**
- * by Szczepan Faber, created at: 11/21/12
- */
 class ProjectDependenciesIntegrationTest extends AbstractDependencyResolutionTest {
 
     @Issue("GRADLE-2477") //this is a feature on its own but also covers one of the reported issues
@@ -59,7 +56,7 @@ class ProjectDependenciesIntegrationTest extends AbstractDependencyResolutionTes
     def "configuring project dependencies by map is validated"() {
         settingsFile << "include 'impl'"
         buildFile << """
-            allprojects { configurations.add('conf') }
+            allprojects { configurations.create('conf') }
             task extraKey << {
                 def dep = dependencies.project(path: ":impl", configuration: ":conf", foo: "bar")
                 assert dep.foo == "bar"
diff --git a/subprojects/core-impl/src/integTest/groovy/org/gradle/integtests/resolve/ProjectDependencyResolveIntegrationTest.groovy b/subprojects/core-impl/src/integTest/groovy/org/gradle/integtests/resolve/ProjectDependencyResolveIntegrationTest.groovy
index a7f7f2c..597c97e 100644
--- a/subprojects/core-impl/src/integTest/groovy/org/gradle/integtests/resolve/ProjectDependencyResolveIntegrationTest.groovy
+++ b/subprojects/core-impl/src/integTest/groovy/org/gradle/integtests/resolve/ProjectDependencyResolveIntegrationTest.groovy
@@ -148,17 +148,18 @@ allprojects {
 project(":a") {
     configurations { 'default' {} }
     dependencies { 'default' 'group:externalA:1.5' }
-    task aJar(type: Jar) { }
-    artifacts { 'default' aJar }
+    task aJar(type: Jar) { baseName='a' }
+    task bJar(type: Jar) { baseName='b' }
+    artifacts { 'default' aJar, bJar }
 }
 
 project(":b") {
     configurations { compile }
-    dependencies { compile(project(':a')) { artifact { name = 'a'; type = 'jar' } } }
+    dependencies { compile(project(':a')) { artifact { name = 'b'; type = 'jar' } } }
     task test {
         inputs.files configurations.compile
         doFirst {
-            assert configurations.compile.files.collect { it.name } == ['a.jar', 'externalA-1.5.jar']
+            assert configurations.compile.files.collect { it.name } == ['b.jar', 'externalA-1.5.jar']
         }
     }
 }
@@ -168,6 +169,38 @@ project(":b") {
         succeeds 'test'
     }
 
+    public void "reports project dependency that refers to an unknown artifact"() {
+        given:
+        file('settings.gradle') << """
+include 'a', 'b'
+"""
+
+        and:
+        buildFile << """
+allprojects { group = 'test' }
+project(":a") {
+    configurations { 'default' {} }
+}
+
+project(":b") {
+    configurations { compile }
+    dependencies { compile(project(':a')) { artifact { name = 'b'; type = 'jar' } } }
+    task test {
+        inputs.files configurations.compile
+        doFirst {
+            assert configurations.compile.files.collect { it.name } == ['a-b.jar', 'externalA-1.5.jar']
+        }
+    }
+}
+"""
+
+        expect:
+        fails 'test'
+
+        and:
+        failure.assertResolutionFailure(":b:compile").assertHasCause("Artifact 'test:a:unspecified at jar' not found.")
+    }
+
     public void "non-transitive project dependency includes only the artifacts of the target configuration"() {
         given:
         mavenRepo.module("group", "externalA", 1.5).publish()
diff --git a/subprojects/core-impl/src/integTest/groovy/org/gradle/integtests/resolve/ResolutionResultApiIntegrationTest.groovy b/subprojects/core-impl/src/integTest/groovy/org/gradle/integtests/resolve/ResolutionResultApiIntegrationTest.groovy
new file mode 100644
index 0000000..dd2403a
--- /dev/null
+++ b/subprojects/core-impl/src/integTest/groovy/org/gradle/integtests/resolve/ResolutionResultApiIntegrationTest.groovy
@@ -0,0 +1,75 @@
+/*
+ * Copyright 2012 the original author or authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS 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.resolve
+
+import org.gradle.integtests.fixtures.AbstractDependencyResolutionTest
+
+import static org.gradle.util.TextUtil.toPlatformLineSeparators
+
+class ResolutionResultApiIntegrationTest extends AbstractDependencyResolutionTest {
+
+    /*
+    The ResolutionResult API is also covered by the dependency report integration tests.
+     */
+
+    def "selection reasons are described"() {
+        given:
+        mavenRepo.module("org", "leaf", 1.0).publish()
+        mavenRepo.module("org", "leaf", 2.0).publish()
+        mavenRepo.module("org", "foo", 0.5).publish()
+
+        mavenRepo.module("org", "foo", 1.0).dependsOn('org', 'leaf', '1.0').publish()
+        mavenRepo.module("org", "bar", 1.0).dependsOn('org', 'leaf', '2.0').publish()
+        mavenRepo.module("org", "baz", 1.0).dependsOn('org', 'foo',  '1.0').publish()
+
+        file("settings.gradle") << "rootProject.name = 'cool-project'"
+
+        file("build.gradle") << """
+            version = '5.0'
+            repositories {
+                maven { url "${mavenRepo.uri}" }
+            }
+            configurations {
+                conf
+            }
+            configurations.conf.resolutionStrategy.force 'org:leaf:2.0'
+            dependencies {
+                conf 'org:foo:0.5', 'org:bar:1.0', 'org:baz:1.0'
+            }
+            task resolutionResult << {
+                def result = configurations.conf.incoming.resolutionResult
+                result.allModuleVersions {
+                    println it.id.name + ":" + it.id.version + " " + it.selectionReason.description
+                }
+            }
+        """
+
+        when:
+        run "resolutionResult"
+
+        then:
+        output.contains(toPlatformLineSeparators("""
+cool-project:5.0 root
+foo:1.0 conflict resolution
+leaf:2.0 forced
+bar:1.0 requested
+baz:1.0 requested
+"""))
+    }
+}
diff --git a/subprojects/core-impl/src/integTest/groovy/org/gradle/integtests/resolve/ResolutionStrategySamplesIntegrationTest.groovy b/subprojects/core-impl/src/integTest/groovy/org/gradle/integtests/resolve/ResolutionStrategySamplesIntegrationTest.groovy
index aae6410..81885f3 100644
--- a/subprojects/core-impl/src/integTest/groovy/org/gradle/integtests/resolve/ResolutionStrategySamplesIntegrationTest.groovy
+++ b/subprojects/core-impl/src/integTest/groovy/org/gradle/integtests/resolve/ResolutionStrategySamplesIntegrationTest.groovy
@@ -20,10 +20,7 @@ import org.gradle.integtests.fixtures.AbstractIntegrationSpec
 import org.gradle.integtests.fixtures.Sample
 import org.junit.Rule
 
-/**
- * @author Szczepan Faber, @date 03.03.11
- */
-class ResolutionStrategySamplesIntegrationTest extends AbstractIntegrationSpec {
+public class ResolutionStrategySamplesIntegrationTest extends AbstractIntegrationSpec {
 
     @Rule public final Sample sample = new Sample(temporaryFolder, 'userguide/artifacts/resolutionStrategy')
 
diff --git a/subprojects/core-impl/src/integTest/groovy/org/gradle/integtests/resolve/ResolveCrossVersionIntegrationTest.groovy b/subprojects/core-impl/src/integTest/groovy/org/gradle/integtests/resolve/ResolveCrossVersionIntegrationTest.groovy
index 6883df0..81d178b 100644
--- a/subprojects/core-impl/src/integTest/groovy/org/gradle/integtests/resolve/ResolveCrossVersionIntegrationTest.groovy
+++ b/subprojects/core-impl/src/integTest/groovy/org/gradle/integtests/resolve/ResolveCrossVersionIntegrationTest.groovy
@@ -16,14 +16,26 @@
 package org.gradle.integtests.resolve
 
 import org.gradle.integtests.fixtures.CrossVersionIntegrationSpec
+import org.gradle.test.fixtures.server.http.HttpServer
+import org.junit.Rule
 
 class ResolveCrossVersionIntegrationTest extends CrossVersionIntegrationSpec {
+    @Rule HttpServer server = new HttpServer()
 
     def "can upgrade and downgrade Gradle version"() {
         given:
+        mavenRepo.module("test", "io", "1.4").publish()
+        mavenRepo.module("test", "lang", "2.4").publish()
+        mavenRepo.module("test", "lang", "2.6").publish()
+
+        and:
+        server.start()
+        server.allowGetOrHead("/repo", mavenRepo.rootDir)
+
+        and:
         buildFile << """
 repositories {
-    mavenCentral()
+    mavenRepo urls: "http://localhost:${server.port}/repo"
 }
 
 configurations {
@@ -31,12 +43,12 @@ configurations {
 }
 
 dependencies {
-    compile 'commons-io:commons-io:1.4'
-    compile 'commons-lang:commons-lang:2.+'
+    compile 'test:io:1.4'
+    compile 'test:lang:2.+'
 }
 
 task check << {
-    assert configurations.compile*.name as Set == ['commons-io-1.4.jar', 'commons-lang-2.6.jar'] as Set
+    assert configurations.compile*.name as Set == ['io-1.4.jar', 'lang-2.6.jar'] as Set
 }
 """
 
diff --git a/subprojects/core-impl/src/integTest/groovy/org/gradle/integtests/resolve/ResolvedConfigurationIntegrationTest.groovy b/subprojects/core-impl/src/integTest/groovy/org/gradle/integtests/resolve/ResolvedConfigurationIntegrationTest.groovy
index 8c1b112..4fbb47a 100644
--- a/subprojects/core-impl/src/integTest/groovy/org/gradle/integtests/resolve/ResolvedConfigurationIntegrationTest.groovy
+++ b/subprojects/core-impl/src/integTest/groovy/org/gradle/integtests/resolve/ResolvedConfigurationIntegrationTest.groovy
@@ -20,13 +20,13 @@ import org.gradle.api.artifacts.LenientConfiguration
 import org.gradle.api.internal.project.DefaultProject
 import org.gradle.api.specs.Specs
 import org.gradle.integtests.fixtures.AbstractIntegrationTest
-import org.gradle.util.HelperUtil
+import org.gradle.util.TestUtil
 import org.junit.Before
 import org.junit.Test
 
 public class ResolvedConfigurationIntegrationTest extends AbstractIntegrationTest {
-    def DefaultProject project = HelperUtil.createRootProject()
-    def Project childProject = HelperUtil.createChildProject(project, "child", new File("."))
+    def DefaultProject project = TestUtil.createRootProject()
+    def Project childProject = TestUtil.createChildProject(project, "child", new File("."))
 
     @Before
     public void boringSetup() {
diff --git a/subprojects/core-impl/src/integTest/groovy/org/gradle/integtests/resolve/VersionConflictResolutionIntegrationTest.groovy b/subprojects/core-impl/src/integTest/groovy/org/gradle/integtests/resolve/VersionConflictResolutionIntegrationTest.groovy
index 0d4da25..e249531 100644
--- a/subprojects/core-impl/src/integTest/groovy/org/gradle/integtests/resolve/VersionConflictResolutionIntegrationTest.groovy
+++ b/subprojects/core-impl/src/integTest/groovy/org/gradle/integtests/resolve/VersionConflictResolutionIntegrationTest.groovy
@@ -21,10 +21,7 @@ import spock.lang.Issue
 import static org.gradle.util.TextUtil.toPlatformLineSeparators
 import static org.hamcrest.Matchers.containsString
 
-/**
- * @author Szczepan Faber, @date 03.03.11
- */
-class VersionConflictResolutionIntegrationTest extends AbstractIntegrationSpec {
+public class VersionConflictResolutionIntegrationTest extends AbstractIntegrationSpec {
 
     void "strict conflict resolution should fail due to conflict"() {
         mavenRepo.module("org", "foo", '1.3.3').publish()
@@ -172,6 +169,37 @@ task checkDeps << {
         run("checkDeps")
     }
 
+    @Issue("GRADLE-2890")
+    void "selects latest from multiple conflicts"() {
+        mavenRepo.module("org", "child", '1').publish()
+        mavenRepo.module("org", "child", '2').publish()
+        mavenRepo.module("org", "parent", '1').dependsOn("org", "child", "1").publish()
+        mavenRepo.module("org", "parent", '2').dependsOn("org", "child", "2").publish()
+        mavenRepo.module("org", "dep", '2').dependsOn("org", "parent", "2").publish()
+
+        buildFile << """
+repositories {
+    maven { url "${mavenRepo.uri}" }
+}
+configurations {
+    compile
+}
+dependencies {
+    compile 'org:parent:1'
+    compile 'org:child:2'
+    compile 'org:dep:2'
+}
+task checkDeps(dependsOn: configurations.compile) << {
+    assert configurations.compile*.name == ['child-2.jar', 'dep-2.jar', 'parent-2.jar']
+    configurations.compile.resolvedConfiguration.firstLevelModuleDependencies*.name
+    configurations.compile.incoming.resolutionResult.allModuleVersions*.id
+}
+"""
+
+        expect:
+        run("checkDeps")
+    }
+
     void "resolves dynamic dependency before resolving conflict"() {
         mavenRepo.module("org", "external", "1.2").publish()
         mavenRepo.module("org", "external", "1.4").publish()
@@ -445,7 +473,7 @@ task checkDeps << {
             }
 
         task checkDeps << {
-            assert configurations.conf*.name == ['a-1.0.jar', 'b-1.0.jar', 'target-child-1.0.jar', 'target-1.0.jar', 'in-conflict-2.0.jar', 'b-child-1.0.jar']
+            assert configurations.conf*.name == ['a-1.0.jar', 'b-1.0.jar', 'b-child-1.0.jar', 'target-1.0.jar', 'in-conflict-2.0.jar', 'target-child-1.0.jar']
             def result = configurations.conf.incoming.resolutionResult
             assert result.allModuleVersions.size() == 7
             def a = result.allModuleVersions.find { it.id.name == 'a' }
@@ -506,7 +534,6 @@ task checkDeps << {
         run("dependencies")
 
         then:
-        //TODO SF proper assertions
         output.contains(toPlatformLineSeparators("""
 childFirst
 +--- org:d:1.0
@@ -530,4 +557,118 @@ parentFirst
 \\--- org:f:1.0
      \\--- org:x:2.0 FAILED"""))
     }
+
+    @Issue("GRADLE-2752")
+    void "does not replace root module when earlier version of root module is requested"() {
+        mavenRepo.module("org", "test", "1.2").publish()
+        mavenRepo.module("org", "other", "1.7").dependsOn("org", "test", "1.2").publish()
+
+        settingsFile << "rootProject.name= 'test'"
+
+        buildFile << """
+apply plugin: 'java'
+
+group "org"
+version "1.3"
+
+repositories {
+    maven { url "${mavenRepo.uri}" }
+}
+
+dependencies {
+    compile "org:other:1.7"
+}
+
+task checkDeps(dependsOn: configurations.compile) << {
+    assert configurations.compile*.name == ['other-1.7.jar', 'test-1.3.jar']
+
+    def result = configurations.compile.incoming.resolutionResult
+    assert result.allModuleVersions.size() == 2
+
+    def root = result.root
+    assert root.id.version == '1.3'
+    assert root.selectionReason.description == 'root'
+    assert !root.selectionReason.conflictResolution //current behavior, feels incorrect
+
+    def other = result.allModuleVersions.find { it.id.name == 'other' }
+
+    assert root.dependencies*.selected == [other]
+    assert other.dependencies*.selected == [root]
+}
+"""
+
+        expect:
+        run("checkDeps")
+    }
+
+    void "module is required only by selected conflicting version and in turn requires evicted conflicting version"() {
+        /*
+            a2 -> b1 -> c1
+            a1
+            c2
+         */
+        mavenRepo.module("org", "a", "1").publish()
+        mavenRepo.module("org", "a", "2").dependsOn("org", "b", "1").publish()
+        mavenRepo.module("org", "b", "1").dependsOn("org", "c", "1").publish()
+        mavenRepo.module("org", "c", "1").publish()
+        mavenRepo.module("org", "c", "2").publish()
+
+        settingsFile << "rootProject.name= 'test'"
+
+        buildFile << """
+repositories {
+    maven { url "${mavenRepo.uri}" }
+}
+configurations {
+    compile
+}
+dependencies {
+    compile "org:a:2"
+    compile "org:a:1"
+    compile "org:c:2"
+}
+
+task checkDeps(dependsOn: configurations.compile) << {
+    assert configurations.compile*.name == ['a-2.jar', 'c-2.jar', 'b-1.jar']
+    assert configurations.compile.incoming.resolutionResult.allModuleVersions.find { it.id.name == 'b' }.dependencies.size() == 1
+}
+"""
+
+        expect:
+        run("checkDeps")
+    }
+
+    @Issue("GRADLE-2738")
+    def "resolution fails when any selector cannot be resolved"() {
+        given:
+        //only 1.5 published:
+        mavenRepo.module("org", "leaf", "1.5").publish()
+
+        mavenRepo.module("org", "c", "1.0").dependsOn("org", "leaf", "2.0+").publish()
+        mavenRepo.module("org", "a", "1.0").dependsOn("org", "leaf", "1.0").publish()
+        mavenRepo.module("org", "b", "1.0").dependsOn("org", "leaf", "[1.5,1.9]").publish()
+
+        settingsFile << "rootProject.name = 'broken'"
+        buildFile << """
+            version = 12
+            repositories {
+                maven { url "${mavenRepo.uri}" }
+            }
+            configurations {
+                conf
+            }
+            dependencies {
+                conf 'org:a:1.0', 'org:b:1.0', 'org:c:1.0'
+            }
+            task resolve << {
+                configurations.conf.files
+            }
+        """
+
+        when:
+        runAndFail "resolve"
+
+        then:
+        failure.assertResolutionFailure(":conf").assertFailedDependencyRequiredBy(":broken:12 > org:c:1.0")
+    }
 }
diff --git a/subprojects/core-impl/src/integTest/groovy/org/gradle/integtests/resolve/artifactreuse/AbstractCacheReuseCrossVersionIntegrationTest.groovy b/subprojects/core-impl/src/integTest/groovy/org/gradle/integtests/resolve/artifactreuse/AbstractCacheReuseCrossVersionIntegrationTest.groovy
index 285c1f5..0e2295d 100644
--- a/subprojects/core-impl/src/integTest/groovy/org/gradle/integtests/resolve/artifactreuse/AbstractCacheReuseCrossVersionIntegrationTest.groovy
+++ b/subprojects/core-impl/src/integTest/groovy/org/gradle/integtests/resolve/artifactreuse/AbstractCacheReuseCrossVersionIntegrationTest.groovy
@@ -22,9 +22,6 @@ import org.gradle.api.internal.artifacts.ivyservice.DefaultCacheLockingManager
 import org.gradle.integtests.fixtures.CrossVersionIntegrationSpec
 import org.gradle.integtests.fixtures.executer.UnderDevelopmentGradleDistribution
 
-/**
- * by Szczepan Faber, created at: 11/27/12
- */
 abstract class AbstractCacheReuseCrossVersionIntegrationTest extends CrossVersionIntegrationSpec {
 
     /**
diff --git a/subprojects/core-impl/src/integTest/groovy/org/gradle/integtests/resolve/artifactreuse/AliasedArtifactResolutionIntegrationTest.groovy b/subprojects/core-impl/src/integTest/groovy/org/gradle/integtests/resolve/artifactreuse/AliasedArtifactResolutionIntegrationTest.groovy
index 8adfc88..7859a85 100644
--- a/subprojects/core-impl/src/integTest/groovy/org/gradle/integtests/resolve/artifactreuse/AliasedArtifactResolutionIntegrationTest.groovy
+++ b/subprojects/core-impl/src/integTest/groovy/org/gradle/integtests/resolve/artifactreuse/AliasedArtifactResolutionIntegrationTest.groovy
@@ -64,11 +64,10 @@ class AliasedArtifactResolutionIntegrationTest extends AbstractDependencyResolut
 
         when:
         def projectBModuleRepo2 = mavenRepo2.module('org.name', 'projectB', '1.0').publish()
-        def projectBArtifactRepo2 = projectBModuleRepo2.artifact
         projectBModuleRepo2.pom.expectHead()
         projectBModuleRepo2.pom.sha1.expectGet()
-        projectBArtifactRepo2.expectHead()
-        projectBArtifactRepo2.sha1.expectGet()
+        projectBModuleRepo2.artifact.expectHead()
+        projectBModuleRepo2.artifact.sha1.expectGet()
 
         then:
         succeedsWith 'mavenRepository2'
@@ -98,7 +97,7 @@ class AliasedArtifactResolutionIntegrationTest extends AbstractDependencyResolut
         when:
         def projectBRepo1 = mavenRepo1.module('org.name', 'projectB', '1.0').publish()
         projectBRepo1.pom.expectGet()
-        projectBRepo1.getArtifact().expectGet()
+        projectBRepo1.artifact.expectGet()
 
         then:
         succeedsWith 'mavenRepository1'
@@ -125,9 +124,8 @@ class AliasedArtifactResolutionIntegrationTest extends AbstractDependencyResolut
         when:
         def projectBRepo2 = mavenRepo1.module('org.name', 'projectB', '1.0').publish()
         projectBRepo2.pom.expectGet()
-        def projectBRepo2Artifact = projectBRepo2.artifact
-        projectBRepo2Artifact.expectHead()
-        projectBRepo2Artifact.sha1.expectGet()
+        projectBRepo2.artifact.expectHead()
+        projectBRepo2.artifact.sha1.expectGet()
 
         then:
         succeedsWith 'mavenRepository1'
@@ -153,7 +151,7 @@ class AliasedArtifactResolutionIntegrationTest extends AbstractDependencyResolut
         when:
         def projectBRepo1 = mavenRepo1.module('org.name', 'projectB', '1.0').publish()
         projectBRepo1.pom.expectGet()
-        projectBRepo1.getArtifact().expectGet()
+        projectBRepo1.artifact.expectGet()
 
         then:
         succeedsWith 'mavenRepository1'
@@ -175,7 +173,7 @@ class AliasedArtifactResolutionIntegrationTest extends AbstractDependencyResolut
         when:
         def projectBRepo1 = mavenRepo1.module('org.name', 'projectB', '1.0').publish()
         projectBRepo1.pom.expectGet()
-        projectBRepo1.getArtifact().expectGet()
+        projectBRepo1.artifact.expectGet()
 
         then:
         succeedsWith 'mavenRepository1'
@@ -186,10 +184,9 @@ class AliasedArtifactResolutionIntegrationTest extends AbstractDependencyResolut
         projectBRepo2.pom.sha1.expectGet()
         projectBRepo2.pom.expectGet()
 
-        def projRepo2BArtifact = projectBRepo2.artifact
-        projRepo2BArtifact.expectHead()
+        projectBRepo2.artifact.expectHead()
         projectBRepo2.artifact.sha1.expectGet()
-        projRepo2BArtifact.expectGet()
+        projectBRepo2.artifact.expectGet()
 
         then:
         succeedsWith 'mavenRepository2'
diff --git a/subprojects/core-impl/src/integTest/groovy/org/gradle/integtests/resolve/caching/CachingDependencyMetadataInMemoryIntegrationTest.groovy b/subprojects/core-impl/src/integTest/groovy/org/gradle/integtests/resolve/caching/CachingDependencyMetadataInMemoryIntegrationTest.groovy
new file mode 100644
index 0000000..f07bf0a
--- /dev/null
+++ b/subprojects/core-impl/src/integTest/groovy/org/gradle/integtests/resolve/caching/CachingDependencyMetadataInMemoryIntegrationTest.groovy
@@ -0,0 +1,197 @@
+/*
+ * Copyright 2013 the original author or authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS 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.resolve.caching
+
+import org.gradle.integtests.fixtures.AbstractDependencyResolutionTest
+import org.gradle.test.fixtures.ivy.IvyFileRepository
+import spock.lang.Ignore
+
+class CachingDependencyMetadataInMemoryIntegrationTest extends AbstractDependencyResolutionTest {
+
+    def "descriptor and artifact is cached in memory"() {
+        given:
+        mavenRepo.module("org", "lib").publish()
+
+        file("build.gradle") << """
+            configurations {
+                one
+                two
+            }
+            repositories {
+                ivy { url "${mavenRepo.uri}" }
+            }
+            dependencies {
+                one 'org:lib:1.0'
+                two 'org:lib:1.0'
+            }
+            //runs first and resolves
+            task resolveOne << {
+                configurations.one.files
+            }
+            //runs second, purges repo
+            task purgeRepo(type: Delete, dependsOn: resolveOne) {
+                delete "${mavenRepo.uri}"
+            }
+            //runs last, still works even thoug local repo is empty
+            task resolveTwo(dependsOn: purgeRepo) << {
+                println "Resolved " + configurations.two.files*.name
+            }
+        """
+
+        when:
+        run "resolveTwo"
+
+        then:
+        output.contains 'Resolved [lib-1.0.jar]'
+    }
+
+    def "descriptors and artifacts are cached across projects and repositories"() {
+        given:
+        ivyRepo.module("org", "lib").publish()
+
+        file("settings.gradle") << "include 'impl'"
+
+        file("build.gradle") << """
+            allprojects {
+                configurations { conf }
+                repositories { ivy { url "${ivyRepo.uri}" } }
+                dependencies { conf 'org:lib:1.0' }
+                task resolveConf << { println path + " " + configurations.conf.files*.name }
+            }
+            task purgeRepo(type: Delete, dependsOn: ':impl:resolveConf') {
+                delete "${ivyRepo.uri}"
+            }
+            resolveConf.dependsOn purgeRepo
+        """
+
+        when:
+        run "resolveConf"
+
+        then:
+        output.contains ':impl:resolveConf [lib-1.0.jar]'
+        output.contains ':resolveConf [lib-1.0.jar]'
+    }
+
+    def "descriptors and artifacts are separated for different repositories"() {
+        given:
+        ivyRepo.module("org", "lib").publish()
+        def ivyRepo2 = new IvyFileRepository(file("ivy-repo2"))
+        ivyRepo2.module("org", "lib", "2.0").publish() //different version of lib
+
+        file("settings.gradle") << "include 'impl'"
+
+        file("build.gradle") << """
+            allprojects {
+                configurations { conf }
+                dependencies { conf 'org:lib:1.0' }
+                task resolveConf << { println "\$path " + configurations.conf.files*.name }
+            }
+            repositories { ivy { url "${ivyRepo.uri}" } }
+            project(":impl") {
+                repositories { ivy { url "${ivyRepo2.uri}" } }
+                tasks.resolveConf.dependsOn(":resolveConf")
+            }
+        """
+
+        when:
+        runAndFail ":impl:resolveConf"
+
+        then:
+        output.contains ':resolveConf [lib-1.0.jar]'
+        //uses different repo that does not contain this dependency
+        failure.assertResolutionFailure(":impl:conf").assertHasCause("Could not find org:lib:1.0")
+    }
+
+    @Ignore //TODO SF rework or remove this test
+    def "snapshot artifacts are only cached per build"() {
+        given:
+        file("provider/build.gradle") << """
+            apply plugin: 'java'
+            apply plugin: 'maven'
+            group = 'org'
+            archivesBaseName = 'provider'
+            version = '1.0-SNAPSHOT'
+            repositories { maven { url "$mavenRepo.uri" } }
+        """
+        file("provider/src/main/java/Name.java") << """public class Name {
+            public String toString() { return "foo"; }
+        }"""
+
+        when:
+        inDirectory "provider"; run "install"
+
+        then:
+        noExceptionThrown()
+
+        when:
+        file("consumer/build.gradle") << """
+            buildscript {
+                repositories { mavenLocal() }
+                dependencies { classpath 'org:provider:1.0-SNAPSHOT' }
+            }
+            task printName << { println "Name: " + new Name() }
+        """
+
+        inDirectory("consumer"); run "printName"
+
+        then:
+        output.contains "Name: foo"
+
+        when:
+        //change the class
+        file("provider/src/main/java/Name.java").text = """public class Name {
+            public String toString() { return "updated"; }
+        }"""
+
+        inDirectory("provider"); run "install"
+
+        and:
+        inDirectory("consumer"); run "printName"
+
+        then:
+        output.contains "Name: updated" //uses updated artifact
+    }
+
+    def "cache expires at the end of build"() {
+        given:
+        ivyRepo.module("org", "dependency").publish()
+        ivyRepo.module("org", "lib").publish()
+
+        file("build.gradle") << """
+            configurations {
+                configurations { conf }
+                repositories { ivy { url "${ivyRepo.uri}" } }
+                dependencies { conf 'org:lib:1.0' }
+            }
+        """
+
+        when:
+        run "dependencies", "--configuration", "conf"
+
+        then:
+        !output.contains("org:dependency:1.0")
+
+        when:
+        ivyRepo.module("org", "lib").dependsOn("org", "dependency", "1.0").publish()
+        run "dependencies", "--configuration", "conf"
+
+        then:
+        output.contains("org:dependency:1.0")
+    }
+}
diff --git a/subprojects/core-impl/src/integTest/groovy/org/gradle/integtests/resolve/custom/FileSystemResolverIntegrationTest.groovy b/subprojects/core-impl/src/integTest/groovy/org/gradle/integtests/resolve/custom/FileSystemResolverIntegrationTest.groovy
index 1a8b161..652edfe 100644
--- a/subprojects/core-impl/src/integTest/groovy/org/gradle/integtests/resolve/custom/FileSystemResolverIntegrationTest.groovy
+++ b/subprojects/core-impl/src/integTest/groovy/org/gradle/integtests/resolve/custom/FileSystemResolverIntegrationTest.groovy
@@ -19,9 +19,8 @@ package org.gradle.integtests.resolve.custom
 import org.gradle.integtests.fixtures.AbstractIntegrationSpec
 
 class FileSystemResolverIntegrationTest extends AbstractIntegrationSpec {
-
     def "file system resolvers use item at source by default"() {
-        when:
+        given:
         def module = ivyRepo.module("group", "projectA", "1.2")
         module.publish()
         def jar = module.jarFile
@@ -45,16 +44,21 @@ class FileSystemResolverIntegrationTest extends AbstractIntegrationSpec {
             }
         """
 
+        when:
+        executer.withDeprecationChecksDisabled()
+        run 'echoContent'
+
         then:
-        succeeds 'echoContent'
         scrapeValue("content") == "1"
         scrapeValue("path") == jar.canonicalPath
 
         when:
         jar.text = "2"
 
+        executer.withDeprecationChecksDisabled()
+        run 'echoContent'
+
         then:
-        succeeds 'echoContent'
         scrapeValue("content") == "2"
         scrapeValue("path") == jar.canonicalPath
     }
diff --git a/subprojects/core-impl/src/integTest/groovy/org/gradle/integtests/resolve/custom/IvySFtpResolverIntegrationTest.groovy b/subprojects/core-impl/src/integTest/groovy/org/gradle/integtests/resolve/custom/IvySFtpResolverIntegrationTest.groovy
index 67e2a21..2aed311 100644
--- a/subprojects/core-impl/src/integTest/groovy/org/gradle/integtests/resolve/custom/IvySFtpResolverIntegrationTest.groovy
+++ b/subprojects/core-impl/src/integTest/groovy/org/gradle/integtests/resolve/custom/IvySFtpResolverIntegrationTest.groovy
@@ -28,7 +28,7 @@ class IvySFtpResolverIntegrationTest extends AbstractIntegrationSpec {
 
     @Rule ProgressLoggingFixture progressLogging = new ProgressLoggingFixture(executer, temporaryFolder)
 
-    def "setup"() {
+    def setup() {
         requireOwnGradleUserHomeDir()
     }
 
@@ -58,7 +58,8 @@ task listJars << {
 }
 """
         when:
-        succeeds 'listJars'
+        executer.withDeprecationChecksDisabled()
+        run 'listJars'
 
         then:
         server.fileRequests == ["repos/libs/group/projectA/1.2/ivy-1.2.xml",
@@ -70,7 +71,8 @@ task listJars << {
 
         when:
         server.clearRequests()
-        succeeds 'listJars'
+        executer.withDeprecationChecksDisabled()
+        run 'listJars'
 
         then:
         server.fileRequests.empty
diff --git a/subprojects/core-impl/src/integTest/groovy/org/gradle/integtests/resolve/custom/IvyUrlResolverIntegrationTest.groovy b/subprojects/core-impl/src/integTest/groovy/org/gradle/integtests/resolve/custom/IvyUrlResolverIntegrationTest.groovy
index f9d2964..5a07eac 100644
--- a/subprojects/core-impl/src/integTest/groovy/org/gradle/integtests/resolve/custom/IvyUrlResolverIntegrationTest.groovy
+++ b/subprojects/core-impl/src/integTest/groovy/org/gradle/integtests/resolve/custom/IvyUrlResolverIntegrationTest.groovy
@@ -24,7 +24,7 @@ class IvyUrlResolverIntegrationTest extends AbstractDependencyResolutionTest {
     @Rule ProgressLoggingFixture progressLogging = new ProgressLoggingFixture(executer, temporaryFolder)
 
     def setup() {
-        server.expectUserAgent(null) // custom resolver uses apache/ivy as useragent strings
+        server.expectUserAgent(null) // custom resolver uses apache/ivy as user agent strings
     }
 
     public void "can resolve and cache dependencies from an HTTP Ivy repository"() {
@@ -52,6 +52,7 @@ task listJars << {
         module.expectIvyGet()
         module.expectJarHead()
         module.expectJarGet()
+        executer.withDeprecationChecksDisabled()
 
         then:
         succeeds 'listJars'
@@ -62,6 +63,7 @@ task listJars << {
 
         when:
         server.resetExpectations()
+        executer.withDeprecationChecksDisabled()
 
         // No extra calls for cached dependencies
         then:
@@ -97,6 +99,7 @@ task retrieve(type: Sync) {
         module.expectIvyGet()
         module.expectJarHead()
         module.expectJarGet()
+        executer.withDeprecationChecksDisabled()
 
         run 'retrieve'
 
@@ -114,6 +117,7 @@ task retrieve(type: Sync) {
         module.expectIvyGet()
         module.expectJarHead()
         module.expectJarGet()
+        executer.withDeprecationChecksDisabled()
 
         run 'retrieve'
 
diff --git a/subprojects/core-impl/src/integTest/groovy/org/gradle/integtests/resolve/ivy/IvyBrokenRemoteResolveIntegrationTest.groovy b/subprojects/core-impl/src/integTest/groovy/org/gradle/integtests/resolve/ivy/IvyBrokenRemoteResolveIntegrationTest.groovy
index fca2690..1ee80e4 100644
--- a/subprojects/core-impl/src/integTest/groovy/org/gradle/integtests/resolve/ivy/IvyBrokenRemoteResolveIntegrationTest.groovy
+++ b/subprojects/core-impl/src/integTest/groovy/org/gradle/integtests/resolve/ivy/IvyBrokenRemoteResolveIntegrationTest.groovy
@@ -57,7 +57,7 @@ task showMissing << { println configurations.missing.files }
         succeeds('showMissing')
     }
 
-    public void "reports and recovers from broken module"() {
+    public void "reports and recovers from failed Ivy descriptor download"() {
         server.start()
 
         given:
@@ -176,4 +176,35 @@ task retrieve(type: Sync) {
         succeeds "retrieve"
         file('libs').assertHasDescendants('projectA-1.2.jar')
     }
+
+    public void "reports Ivy descriptor that cannot be parsed"() {
+        server.start()
+        given:
+        buildFile << """
+repositories {
+    ivy {
+        url "${ivyHttpRepo.uri}"
+    }
+}
+configurations { compile }
+dependencies {
+    compile 'group:projectA:1.2'
+}
+task showBroken << { println configurations.compile.files }
+"""
+
+        and:
+        def module = ivyHttpRepo.module('group', 'projectA', '1.2').publish()
+        module.ivyFile.text = "<ivy-module>"
+
+        when:
+        module.expectIvyGet()
+
+        then:
+        fails "showBroken"
+        failure
+            .assertResolutionFailure(":compile")
+            .assertHasCause("Could not parse Ivy file ${module.ivyFileUri}")
+            .assertHasCause("invalid version null")
+    }
 }
diff --git a/subprojects/core-impl/src/integTest/groovy/org/gradle/integtests/resolve/ivy/IvyChangingModuleRemoteResolveIntegrationTest.groovy b/subprojects/core-impl/src/integTest/groovy/org/gradle/integtests/resolve/ivy/IvyChangingModuleRemoteResolveIntegrationTest.groovy
index 2bcdea5..0319d6e 100644
--- a/subprojects/core-impl/src/integTest/groovy/org/gradle/integtests/resolve/ivy/IvyChangingModuleRemoteResolveIntegrationTest.groovy
+++ b/subprojects/core-impl/src/integTest/groovy/org/gradle/integtests/resolve/ivy/IvyChangingModuleRemoteResolveIntegrationTest.groovy
@@ -70,7 +70,6 @@ task retrieve(type: Copy) {
         module.expectIvySha1Get()
         module.expectIvyGet()
         module.expectJarHead()
-        module.expectJarSha1Get()
         module.expectArtifactGet('other')
         moduleB.expectIvyGet()
         moduleB.expectJarGet()
diff --git a/subprojects/core-impl/src/integTest/groovy/org/gradle/integtests/resolve/ivy/IvyComponentMetadataRulesIntegrationTest.groovy b/subprojects/core-impl/src/integTest/groovy/org/gradle/integtests/resolve/ivy/IvyComponentMetadataRulesIntegrationTest.groovy
new file mode 100644
index 0000000..65b2e0b
--- /dev/null
+++ b/subprojects/core-impl/src/integTest/groovy/org/gradle/integtests/resolve/ivy/IvyComponentMetadataRulesIntegrationTest.groovy
@@ -0,0 +1,132 @@
+/*
+ * Copyright 2012 the original author or authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS 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.resolve.ivy
+
+import org.gradle.integtests.fixtures.AbstractDependencyResolutionTest
+
+import static org.hamcrest.Matchers.containsString
+
+class IvyComponentMetadataRulesIntegrationTest extends AbstractDependencyResolutionTest {
+    def setup() {
+        buildFile <<
+"""
+repositories {
+    ivy {
+        url "${ivyRepo.uri}"
+    }
+}
+
+configurations { compile }
+
+dependencies {
+    compile 'org.test:projectA:1.0'
+}
+
+task resolve(type: Sync) {
+    from configurations.compile
+    into 'libs'
+}
+"""
+    }
+
+    def "rule is being passed correct, mutable metadata"() {
+        ivyRepo.module('org.test', 'projectA', '1.0').withStatus("release").publish()
+        buildFile <<
+"""
+dependencies {
+    components {
+        eachComponent { details ->
+            assert details.id.group == "org.test"
+            assert details.id.name == "projectA"
+            assert details.id.version == "1.0"
+            assert details.status == "release"
+            assert details.statusScheme == ["integration", "milestone", "release"]
+
+            details.status "silver" // verify that 'details' is enhanced
+            assert details.status == "silver"
+
+            details.statusScheme = ["bronze", "silver", "gold"]
+            assert details.statusScheme == ["bronze", "silver", "gold"]
+        }
+    }
+}
+"""
+
+        expect:
+        succeeds 'resolve'
+    }
+
+    def "module with custom status can be resolved by adapting status scheme"() {
+        ivyRepo.module('org.test', 'projectA', '1.0').withStatus("silver").publish()
+        buildFile <<
+"""
+dependencies {
+    components {
+        eachComponent { details ->
+            details.statusScheme = ["gold", "silver", "bronze"]
+        }
+    }
+}
+"""
+
+        expect:
+        succeeds 'resolve'
+        file('libs').assertHasDescendants('projectA-1.0.jar')
+    }
+
+    def "resolve fails if status doesn't match default status scheme"() {
+        ivyRepo.module('org.test', 'projectA', '1.0').withStatus("silver").publish()
+
+        expect:
+        fails 'resolve'
+        failure.assertThatCause(containsString("bad status: 'silver'"))
+    }
+
+    def "resolve fails if status doesn't match custom status scheme"() {
+        ivyRepo.module('org.test', 'projectA', '1.0').withStatus("silver").publish()
+        buildFile <<
+"""
+dependencies {
+    components {
+        eachComponent { details ->
+            details.statusScheme = ["gold", "bronze"]
+        }
+    }
+}
+"""
+
+        expect:
+        fails 'resolve'
+        failure.assertThatCause(containsString("bad status: 'silver'"))
+    }
+
+    def "rule can change status"() {
+        ivyRepo.module('org.test', 'projectA', '1.0').withStatus("silver").publish()
+        buildFile <<
+"""
+dependencies {
+    components {
+        eachComponent { details ->
+            details.status = "milestone"
+        }
+    }
+}
+"""
+
+        expect:
+        succeeds 'resolve'
+    }
+}
diff --git a/subprojects/core-impl/src/integTest/groovy/org/gradle/integtests/resolve/ivy/IvyCustomStatusLatestVersionIntegrationTest.groovy b/subprojects/core-impl/src/integTest/groovy/org/gradle/integtests/resolve/ivy/IvyCustomStatusLatestVersionIntegrationTest.groovy
new file mode 100644
index 0000000..0a4e444
--- /dev/null
+++ b/subprojects/core-impl/src/integTest/groovy/org/gradle/integtests/resolve/ivy/IvyCustomStatusLatestVersionIntegrationTest.groovy
@@ -0,0 +1,62 @@
+/*
+ * Copyright 2013 the original author or authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS 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.resolve.ivy
+
+import org.gradle.integtests.fixtures.AbstractIntegrationSpec
+
+class IvyCustomStatusLatestVersionIntegrationTest extends AbstractIntegrationSpec {
+    def "latest.xyz selects highest version with given or higher status"() {
+        given:
+        buildFile << """
+repositories {
+    ivy {
+        url "${ivyRepo.uri}"
+    }
+}
+configurations { compile }
+dependencies {
+    compile 'org.test:projectA:latest.$status'
+    components {
+        eachComponent { details ->
+            details.statusScheme = ["bronze", "silver", "gold", "platin"]
+        }
+    }
+}
+
+task retrieve(type: Sync) {
+    from configurations.compile
+    into 'libs'
+}
+"""
+
+        when:
+        ivyRepo.module('org.test', 'projectA', '1.1').withStatus('bronze').publish()
+        ivyRepo.module('org.test', 'projectA', '1.3').withStatus('silver').publish()
+        ivyRepo.module('org.test', 'projectA', '1.2').withStatus('gold').publish()
+        ivyRepo.module('org.test', 'projectA', '1.0').withStatus('platin').publish()
+        run 'retrieve'
+
+        then:
+        file('libs').assertHasDescendants("projectA-${version}.jar")
+
+        where:
+        status   | version
+        "bronze" | "1.3"
+        "silver" | "1.3"
+        "gold"   | "1.2"
+        "platin" | "1.0"
+    }
+}
diff --git a/subprojects/core-impl/src/integTest/groovy/org/gradle/integtests/resolve/ivy/IvyDescriptorResolveIntegrationTest.groovy b/subprojects/core-impl/src/integTest/groovy/org/gradle/integtests/resolve/ivy/IvyDescriptorResolveIntegrationTest.groovy
index 858d735..9d1c180 100644
--- a/subprojects/core-impl/src/integTest/groovy/org/gradle/integtests/resolve/ivy/IvyDescriptorResolveIntegrationTest.groovy
+++ b/subprojects/core-impl/src/integTest/groovy/org/gradle/integtests/resolve/ivy/IvyDescriptorResolveIntegrationTest.groovy
@@ -14,7 +14,9 @@
  * limitations under the License.
  */
 package org.gradle.integtests.resolve.ivy
+
 import org.gradle.integtests.fixtures.AbstractDependencyResolutionTest
+import spock.lang.Unroll
 
 class IvyDescriptorResolveIntegrationTest extends AbstractDependencyResolutionTest {
     def "substitutes system properties into ivy descriptor"() {
@@ -54,22 +56,23 @@ task check << {
 
     def "merges values from included descriptor file"() {
         given:
-        final parentModule = ivyRepo.module("org.gradle.parent", "parent_module", "1.1").dependsOn("org.gradle.dep", "dep_module", "1.1").publish()
-        ivyRepo.module("org.gradle.dep", "dep_module", "1.1").publish()
+        server.start()
+        final parentModule = ivyHttpRepo.module("org.gradle.parent", "parent_module", "1.1").dependsOn("org.gradle.dep", "dep_module", "1.1").publish()
+        final depModule = ivyHttpRepo.module("org.gradle.dep", "dep_module", "1.1").publish()
 
-        final module = ivyRepo.module("org.gradle", "test", "1.45")
+        final module = ivyHttpRepo.module("org.gradle", "test", "1.45")
         final extendAttributes = ["organisation": "org.gradle.parent", "module": "parent_module", "revision": "1.1"]
         if (includeLocation) {
-            extendAttributes["location"] = parentModule.ivyFile.absolutePath
+            extendAttributes["location"] = parentModule.ivyFile.toURI().toURL()
         }
         module.withXml {
             asNode().info[0].appendNode("extends", extendAttributes)
         }
         module.publish()
 
-        and:
+        when:
         buildFile << """
-repositories { ivy { url "${ivyRepo.uri}" } }
+repositories { ivy { url "${ivyHttpRepo.uri}" } }
 configurations { compile }
 dependencies {
     compile "org.gradle:test:1.45"
@@ -80,12 +83,131 @@ task check << {
 }
 """
 
-        expect:
+        and:
+        module.expectIvyGet()
+        if (!includeLocation) {
+            parentModule.expectIvyGet()
+        }
+        depModule.expectIvyGet()
+        module.expectJarGet()
+        depModule.expectJarGet()
+
+        then:
+        executer.withArgument("--debug")
+        succeeds "check"
+
+        when:
+        server.resetExpectations()
+
+        then:
+        executer.withArgument("--debug")
         succeeds "check"
 
         where:
-        name | includeLocation
-        "with explicit location" | true
+        name                        | includeLocation
+        "with explicit location"    | true
         "without explicit location" | false
     }
+
+    @Unroll
+    def "excludes transitive dependencies when ivy.xml has dependency declared with #name"() {
+        given:
+
+        ivyRepo.module("org.gradle.dep", "dep_module", "1.134")
+                .dependsOn("org.gradle.one", "mod_one", "1.1")
+                .dependsOn("org.gradle.two", "mod_one", "2.1")
+                .dependsOn("org.gradle.two", "mod_two", "2.2")
+                .publish()
+        ivyRepo.module("org.gradle.one", "mod_one", "1.1").artifact([:]).artifact([type: 'war']).publish()
+        ivyRepo.module("org.gradle.two", "mod_one", "2.1").publish()
+        ivyRepo.module("org.gradle.two", "mod_two", "2.2").publish()
+
+        ivyRepo.module("org.gradle.test", "test_exclude", "1.134")
+                .dependsOn("org.gradle.dep", "dep_module", "1.134")
+                .withXml({
+            asNode().dependencies[0].dependency[0].appendNode("exclude", excludeAttributes)
+        })
+                .publish()
+
+        and:
+        buildFile << """
+repositories { ivy { url "${ivyRepo.uri}" } }
+configurations { compile }
+dependencies {
+    compile "org.gradle.test:test_exclude:1.134"
+}
+
+task check(type: Sync) {
+    into "libs"
+    from configurations.compile
+}
+"""
+
+        when:
+        succeeds "check"
+
+        then:
+        def jars = ['test_exclude-1.134.jar', 'dep_module-1.134.jar'] + transitiveJars
+        file("libs").assertHasDescendants(jars.toArray(new String[0]))
+
+        where:
+        name                       | excludeAttributes                          | transitiveJars
+        "empty exclude"            | [:]                                        | []
+        "unmatched exclude"        | [module: "different"]                      | ['mod_one-1.1.jar', 'mod_one-1.1.war', 'mod_one-2.1.jar', 'mod_two-2.2.jar']
+        "module exclude"           | [module: "mod_one"]                        | ['mod_two-2.2.jar']
+        "org exclude"              | [org: "org.gradle.two"]                    | ['mod_one-1.1.jar', 'mod_one-1.1.war']
+        "module and org exclude"   | [org: "org.gradle.two", module: "mod_one"] | ['mod_one-1.1.jar', 'mod_one-1.1.war', 'mod_two-2.2.jar']
+        "regex module exclude"     | [module: "mod.*"]                          | []
+        "matching config exclude"  | [module: "mod_one", conf: "default,other"] | ['mod_two-2.2.jar']
+        "unmatched config exclude" | [module: "mod_one", conf: "other"]         | ['mod_one-1.1.jar', 'mod_one-1.1.war', 'mod_one-2.1.jar', 'mod_two-2.2.jar']
+//  GRADLE-2674
+//        "type exclude"           | [type: "war"]                              | ['mod_one-1.1.jar', 'mod_one-2.1.jar', 'mod_two-2.2.jar']
+    }
+
+    def "transitive dependencies are only excluded if excluded from each dependency declaration"() {
+//        c -> d,e
+//        a -> c (excludes 'd')
+//        b -> c (excludes 'd', 'e')
+        given:
+        ivyRepo.module("d").publish()
+        ivyRepo.module("e").publish()
+        ivyRepo.module("c").dependsOn("d").dependsOn("e").publish()
+
+        ivyRepo.module("a")
+                .dependsOn("c")
+                .withXml({
+                    asNode().dependencies[0].dependency[0].appendNode("exclude", [module: "d"])
+                })
+                .publish()
+        ivyRepo.module("b")
+                .dependsOn("c")
+                .withXml({
+                    def dep = asNode().dependencies[0].dependency[0]
+                    dep.appendNode("exclude", [module: "d"])
+                    dep.appendNode("exclude", [module: "e"])
+                })
+                .publish()
+
+        and:
+        buildFile << """
+repositories { ivy { url "${ivyRepo.uri}" } }
+configurations {
+    merged
+}
+dependencies {
+    merged "org.gradle.test:a:1.0", "org.gradle.test:b:1.0"
+}
+
+task syncMerged(type: Sync) {
+    from configurations.merged
+    into "libs"
+}
+"""
+
+        when:
+        succeeds "syncMerged"
+
+        then:
+        file("libs").assertHasDescendants(['a-1.0.jar', 'b-1.0.jar', 'c-1.0.jar', 'e-1.0.jar'] as String[])
+    }
 }
diff --git a/subprojects/core-impl/src/integTest/groovy/org/gradle/integtests/resolve/ivy/IvyDynamicRevisionRemoteResolveIntegrationTest.groovy b/subprojects/core-impl/src/integTest/groovy/org/gradle/integtests/resolve/ivy/IvyDynamicRevisionRemoteResolveIntegrationTest.groovy
index a4af2af..164b304 100644
--- a/subprojects/core-impl/src/integTest/groovy/org/gradle/integtests/resolve/ivy/IvyDynamicRevisionRemoteResolveIntegrationTest.groovy
+++ b/subprojects/core-impl/src/integTest/groovy/org/gradle/integtests/resolve/ivy/IvyDynamicRevisionRemoteResolveIntegrationTest.groovy
@@ -253,6 +253,49 @@ task retrieveMilestone(type: Sync) {
         file('milestone').assertHasDescendants('projectA-1.1.jar')
     }
 
+    def "can get latest version from repository with multiple ivyPatterns"() {
+        server.start()
+
+        given:
+        def repo1 = ivyHttpRepo("ivyRepo1")
+        repo1.module('org.test', 'projectA', '1.1').withStatus("integration").publish()
+        def repo1version2 = repo1.module('org.test', 'projectA', '1.2').withStatus("milestone").publish()
+        def repo2 = ivyHttpRepo("ivyRepo2")
+        def repo2version1 = repo2.module('org.test', 'projectA', '1.1').withStatus("milestone").publish()
+        def repo2version3 = repo2.module('org.test', 'projectA', '1.3').withStatus("integration").publish()
+
+        and:
+        buildFile << """
+repositories {
+    ivy {
+        url "${repo1.uri}"
+        ivyPattern "${repo2.uri}/[organisation]/[module]/[revision]/ivy-[revision].xml"
+        artifactPattern "${repo2.uri}/[organisation]/[module]/[revision]/[artifact]-[revision].[ext]"
+    }
+}
+configurations { milestone }
+dependencies {
+  milestone 'org.test:projectA:latest.milestone'
+}
+task retrieveMilestone(type: Sync) {
+  from configurations.milestone
+  into 'milestone'
+}
+"""
+        when:
+        repo1.expectDirectoryListGet("org.test", "projectA")
+        repo2.expectDirectoryListGet("org.test", "projectA")
+        repo2version3.expectIvyGet()
+        repo1version2.expectIvyGet()
+        repo1version2.expectJarGet()
+
+        then:
+        succeeds 'retrieveMilestone'
+
+        and:
+        file('milestone').assertHasDescendants('projectA-1.2.jar')
+    }
+
     def "checks new repositories before returning any cached value"() {
         server.start()
         def repo1 = ivyHttpRepo("repo1")
diff --git a/subprojects/core-impl/src/integTest/groovy/org/gradle/integtests/resolve/ivy/IvyDynamicRevisionResolveIntegrationTest.groovy b/subprojects/core-impl/src/integTest/groovy/org/gradle/integtests/resolve/ivy/IvyDynamicRevisionResolveIntegrationTest.groovy
index b3d4176..ed5974f 100644
--- a/subprojects/core-impl/src/integTest/groovy/org/gradle/integtests/resolve/ivy/IvyDynamicRevisionResolveIntegrationTest.groovy
+++ b/subprojects/core-impl/src/integTest/groovy/org/gradle/integtests/resolve/ivy/IvyDynamicRevisionResolveIntegrationTest.groovy
@@ -20,7 +20,6 @@ import spock.lang.Ignore
 import spock.lang.Issue
 
 class IvyDynamicRevisionResolveIntegrationTest extends AbstractDependencyResolutionTest {
-    @Ignore
     @Issue("GRADLE-2502")
     def "latest.integration selects highest version regardless of status"() {
         given:
@@ -44,7 +43,7 @@ class IvyDynamicRevisionResolveIntegrationTest extends AbstractDependencyResolut
         runAndFail 'retrieve'
 
         then:
-        failureHasCause 'Could not find any version that matches group:group, module:projectA, version:latest.integration.'
+        failureHasCause 'Could not find any version that matches org.test:projectA:latest.integration.'
 
         when:
         ivyRepo.module('org.test', 'projectA', '1.0').withNoMetaData().publish()
@@ -67,8 +66,30 @@ class IvyDynamicRevisionResolveIntegrationTest extends AbstractDependencyResolut
 
         then:
         file('libs').assertHasDescendants('projectA-1.3.jar')
+    }
+
+    @Ignore
+    @Issue("GRADLE-2502")
+    def "latest.integration selects highest version regardless of status even if metadata is missing"() {
+        given:
+        buildFile << """
+  repositories {
+      ivy {
+          url "${ivyRepo.uri}"
+      }
+  }
+  configurations { compile }
+  dependencies {
+      compile 'org.test:projectA:latest.integration'
+  }
+  task retrieve(type: Sync) {
+      from configurations.compile
+      into 'libs'
+  }
+  """
 
         when:
+        ivyRepo.module('org.test', 'projectA', '1.1').withStatus('integration').publish()
         ivyRepo.module('org.test', 'projectA', '1.4').withNoMetaData().publish()
         run 'retrieve'
 
@@ -179,7 +200,6 @@ class IvyDynamicRevisionResolveIntegrationTest extends AbstractDependencyResolut
         file('libs').assertHasDescendants('projectA-1.1.jar')
     }
 
-    @Ignore
     @Issue("GRADLE-2502")
     def "version selector ending in + selects highest matching version"() {
         given:
@@ -228,6 +248,30 @@ class IvyDynamicRevisionResolveIntegrationTest extends AbstractDependencyResolut
 
         then:
         file('libs').assertHasDescendants('projectA-1.2.9.jar')
+    }
+
+    @Ignore
+    @Issue("GRADLE-2502")
+    def "version selector ending in + selects highest matching version even if metadata is missing"() {
+        given:
+        buildFile << """
+  repositories {
+      ivy {
+          url "${ivyRepo.uri}"
+      }
+  }
+  configurations { compile }
+  dependencies {
+      compile 'org.test:projectA:1.2+'
+  }
+  task retrieve(type: Sync) {
+      from configurations.compile
+      into 'libs'
+  }
+  """
+        and:
+        ivyRepo.module('org.test', 'projectA', '1.2.1').publish()
+        ivyRepo.module('org.test', 'projectA', '2.0').publish()
 
         when:
         ivyRepo.module('org.test', 'projectA', '1.2.12').withNoMetaData().publish()
@@ -237,7 +281,6 @@ class IvyDynamicRevisionResolveIntegrationTest extends AbstractDependencyResolut
         file('libs').assertHasDescendants('projectA-1.2.12.jar')
     }
 
-    @Ignore
     @Issue("GRADLE-2502")
     def "version range selects highest matching version"() {
         given:
@@ -258,7 +301,7 @@ class IvyDynamicRevisionResolveIntegrationTest extends AbstractDependencyResolut
   """
         and:
         ivyRepo.module('org.test', 'projectA', '1.1.2').publish()
-        ivyRepo.module('org.test', 'projectA', '2.0').publish()
+        ivyRepo.module('org.test', 'projectA', '2.1').publish()
 
         when:
         runAndFail 'retrieve'
@@ -286,6 +329,29 @@ class IvyDynamicRevisionResolveIntegrationTest extends AbstractDependencyResolut
 
         then:
         file('libs').assertHasDescendants('projectA-1.3.jar')
+    }
+
+    @Ignore
+    @Issue("GRADLE-2502")
+    def "version range selects highest matching version even if metadata is missing"() {
+        given:
+        buildFile << """
+  repositories {
+      ivy {
+          url "${ivyRepo.uri}"
+      }
+  }
+  configurations { compile }
+  dependencies {
+      compile 'org.test:projectA:[1.2,2.0]'
+  }
+  task retrieve(type: Sync) {
+      from configurations.compile
+      into 'libs'
+  }
+  """
+        and:
+        ivyRepo.module('org.test', 'projectA', '1.2.1').publish()
 
         when:
         ivyRepo.module('org.test', 'projectA', '1.3.12').withNoMetaData().publish()
@@ -330,4 +396,53 @@ class IvyDynamicRevisionResolveIntegrationTest extends AbstractDependencyResolut
         then:
         file('libs').assertHasDescendants('projectA-1.1.jar')
     }
+
+    def "can resolve dynamic versions with multiple ivy patterns"() {
+        given:
+        def repo1 = ivyRepo("ivyRepo1")
+        repo1.module('org.test', 'projectA', '1.1').withStatus("integration").publish()
+        repo1.module('org.test', 'projectA', '1.2').withStatus("milestone").publish()
+        def repo2 = ivyRepo("ivyRepo2")
+        repo2.module('org.test', 'projectA', '1.1').withStatus("milestone").publish()
+        repo2.module('org.test', 'projectA', '1.3').withStatus("integration").publish()
+
+        and:
+        buildFile << """
+repositories {
+    ivy {
+        url "${repo1.uri}"
+        ivyPattern "${repo2.uri}/[organisation]/[module]/[revision]/ivy-[revision].xml"
+        artifactPattern "${repo2.uri}/[organisation]/[module]/[revision]/[artifact]-[revision].[ext]"
+    }
+}
+configurations {
+    milestone
+    dynamic
+}
+dependencies {
+  milestone 'org.test:projectA:latest.milestone'
+  dynamic 'org.test:projectA:1.+'
+}
+task retrieveDynamic(type: Sync) {
+  from configurations.dynamic
+  into 'dynamic'
+}
+task retrieveMilestone(type: Sync) {
+  from configurations.milestone
+  into 'milestone'
+}
+"""
+
+        when:
+        run 'retrieveDynamic'
+
+        then:
+        file('dynamic').assertHasDescendants('projectA-1.3.jar')
+
+        when:
+        run 'retrieveMilestone'
+
+        then:
+        file('milestone').assertHasDescendants('projectA-1.2.jar')
+    }
 }
diff --git a/subprojects/core-impl/src/integTest/groovy/org/gradle/integtests/resolve/ivy/IvyMetadataConsistencyIntegrationTest.groovy b/subprojects/core-impl/src/integTest/groovy/org/gradle/integtests/resolve/ivy/IvyMetadataConsistencyIntegrationTest.groovy
new file mode 100644
index 0000000..f67d7c3
--- /dev/null
+++ b/subprojects/core-impl/src/integTest/groovy/org/gradle/integtests/resolve/ivy/IvyMetadataConsistencyIntegrationTest.groovy
@@ -0,0 +1,54 @@
+/*
+ * Copyright 2013 the original author or authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS 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.resolve.ivy
+
+import org.gradle.integtests.fixtures.AbstractDependencyResolutionTest
+
+import static org.hamcrest.CoreMatchers.containsString
+
+class IvyMetadataConsistencyIntegrationTest extends AbstractDependencyResolutionTest {
+    def "latest.integration selects highest version regardless of status"() {
+        given:
+        buildFile << """
+  repositories {
+      ivy {
+          url "${ivyRepo.uri}"
+      }
+  }
+  configurations { compile }
+  dependencies {
+      compile 'org.test:projectA:[1.3,1.5]'
+  }
+  task resolve << {
+      configurations.compile.resolve()
+  }
+  """
+
+        def module = ivyRepo.module('org.test', 'projectA', '1.4')
+        module.publish()
+
+        expect:
+        succeeds 'resolve'
+
+        when:
+        module.ivyFile.setText(module.ivyFile.text.replace('revision="1.4"', 'revision="1.6"'), "utf-8")
+
+        then:
+        fails 'resolve'
+        failure.assertThatCause(containsString("bad version: expected='1.4' found='1.6'"))
+    }
+
+}
\ No newline at end of file
diff --git a/subprojects/core-impl/src/integTest/groovy/org/gradle/integtests/resolve/ivy/IvyModuleResolveIntegrationTest.groovy b/subprojects/core-impl/src/integTest/groovy/org/gradle/integtests/resolve/ivy/IvyModuleResolveIntegrationTest.groovy
new file mode 100644
index 0000000..ee15e89
--- /dev/null
+++ b/subprojects/core-impl/src/integTest/groovy/org/gradle/integtests/resolve/ivy/IvyModuleResolveIntegrationTest.groovy
@@ -0,0 +1,209 @@
+/*
+ * Copyright 2013 the original author or authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS 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.resolve.ivy
+
+import org.gradle.integtests.fixtures.AbstractDependencyResolutionTest
+import spock.lang.Unroll
+
+class IvyModuleResolveIntegrationTest extends AbstractDependencyResolutionTest {
+    def "wildcard on LHS of configuration mapping includes all public configurations of target module"() {
+        given:
+        buildFile << """
+configurations {
+    compile
+}
+dependencies {
+    repositories {
+        ivy { url "${ivyRepo.uri}" }
+    }
+    compile 'ivy.configuration:projectA:1.2'
+}
+task retrieve(type: Sync) {
+  from configurations.compile
+  into 'libs'
+}
+"""
+        when: "projectA uses a wildcard configuration mapping for dependency on projectB"
+        def moduleA = ivyRepo.module('ivy.configuration', 'projectA', '1.2')
+                .configuration('parent')
+                .artifact()
+                .dependsOn(organisation: 'ivy.configuration', module: 'projectB', revision: '1.5', conf: 'runtime->*')
+                .publish()
+
+        ivyRepo.module('ivy.configuration', 'projectB', '1.5')
+                .configuration('child')
+                .configuration('private', visibility: 'private')
+                .artifact()
+                .artifact([name: 'projectB', conf: 'runtime'])
+                .artifact([name: 'projectB-child', conf: 'child'])
+                .artifact([name: 'projectB-private', conf: 'private'])
+                .dependsOn(organisation: 'ivy.configuration', module: 'projectC', revision: '1.7', conf: 'child->*')
+                .dependsOn(organisation: 'ivy.configuration', module: 'projectD', revision: 'broken', conf: 'private->*')
+                .publish()
+
+        ivyRepo.module('ivy.configuration', 'projectC', '1.7')
+                .artifact()
+                .publish()
+
+        and:
+        succeeds 'retrieve'
+
+        then: "artifacts and dependencies from all configurations of projectB are included"
+        file('libs').assertHasDescendants('projectA-1.2.jar', 'projectB-1.5.jar', 'projectB-child-1.5.jar', 'projectC-1.7.jar')
+
+        when: "projectB-1.5 is replaced by conflict resolution with projectB-1.6 that has a different set of configurations"
+
+        ivyRepo.module('ivy.configuration', 'projectB', '1.6')
+                .configuration('other')
+                .artifact([name: 'projectB-other', conf: 'other'])
+                .publish()
+
+        ivyRepo.module('ivy.configuration', 'projectD', '1.0')
+                .dependsOn('ivy.configuration', 'projectB', '1.6')
+                .publish()
+
+        moduleA.dependsOn('ivy.configuration', 'projectD', '1.0').publish()
+
+        and:
+        succeeds 'retrieve'
+
+        then: "we resolve artifacts from projectB-1.6 only"
+        file('libs').assertHasDescendants('projectA-1.2.jar', 'projectB-other-1.6.jar', 'projectD-1.0.jar')
+    }
+
+    @Unroll
+    def "correctly handles configuration mapping rule '#rule'"() {
+        given:
+        buildFile << """
+configurations {
+    compile
+}
+dependencies {
+    repositories {
+        ivy { url "${ivyRepo.uri}" }
+    }
+    compile group: 'ivy.configuration', name: 'projectA', version: '1.2', configuration: 'a'
+}
+task retrieve(type: Sync) {
+  from configurations.compile
+  into 'libs'
+}
+"""
+        ivyRepo.module('ivy.configuration', 'projectA', '1.2')
+                .configuration("parent")
+                .configuration("a", extendsFrom: ["parent"])
+                .configuration("b")
+                .dependsOn(organisation: 'ivy.configuration', module: 'projectB', revision: '1.5', conf: rule)
+                .publish()
+
+        ivyRepo.module('ivy.configuration', 'projectB', '1.5')
+                .configuration('a')
+                .configuration('b')
+                .configuration('c')
+                .configuration('d', visibility: 'private')
+                .artifact([name: 'projectB-a', conf: 'a'])
+                .artifact([name: 'projectB-b', conf: 'b'])
+                .artifact([name: 'projectB-c', conf: 'c'])
+                .artifact([name: 'projectB-d', conf: 'd'])
+                .dependsOn(organisation: 'ivy.configuration', module: 'projectC', revision: '1.7', conf: 'a->default')
+                .dependsOn(organisation: 'ivy.configuration', module: 'projectD', revision: '1.7', conf: 'b->default')
+                .dependsOn(organisation: 'ivy.configuration', module: 'projectE', revision: '1.7', conf: 'd->default')
+                .publish()
+
+        ivyRepo.module('ivy.configuration', 'projectC', '1.7').publish()
+        ivyRepo.module('ivy.configuration', 'projectD', '1.7').publish()
+
+        when:
+        run 'retrieve'
+
+        then:
+        file('libs').assertHasDescendants(* (['projectA-1.2.jar'] + jars))
+
+        where:
+        rule                    | jars
+        "a"                     | ["projectB-a-1.5.jar", "projectC-1.7.jar"]
+        "a->b"                  | ["projectB-b-1.5.jar", "projectD-1.7.jar"]
+        "a,b->b"                | ["projectB-b-1.5.jar", "projectD-1.7.jar"]
+        "parent->a"             | ["projectB-a-1.5.jar", "projectC-1.7.jar"]
+        "a,parent->a"           | ["projectB-a-1.5.jar", "projectC-1.7.jar"]
+        "a->a,b"                | ["projectB-a-1.5.jar", "projectB-b-1.5.jar", "projectC-1.7.jar", "projectD-1.7.jar"]
+        "a;a->b"                | ["projectB-a-1.5.jar", "projectB-b-1.5.jar", "projectC-1.7.jar", "projectD-1.7.jar"]
+        "*->a"                  | ["projectB-a-1.5.jar", "projectC-1.7.jar"]
+        "*->*"                  | ["projectB-a-1.5.jar", "projectB-b-1.5.jar", "projectB-c-1.5.jar", "projectC-1.7.jar", "projectD-1.7.jar"]
+        "*->@"                  | ["projectB-a-1.5.jar", "projectC-1.7.jar"]
+        "a,b->@"                | ["projectB-a-1.5.jar", "projectC-1.7.jar"]
+        "runtime->unknown;%->@" | ["projectB-a-1.5.jar", "projectC-1.7.jar"]
+        "a->a;%->b"             | ["projectB-a-1.5.jar", "projectC-1.7.jar"]
+        "*,!b->b"               | ["projectB-b-1.5.jar", "projectD-1.7.jar"]
+        "b"                     | []
+        "*,!a->a"               | []
+        "a->#"                  | ["projectB-a-1.5.jar", "projectC-1.7.jar"]
+        "parent->#"             | ["projectB-a-1.5.jar", "projectC-1.7.jar"]
+        "*->#"                  | ["projectB-a-1.5.jar", "projectC-1.7.jar"]
+        "*->unknown(a)"         | ["projectB-a-1.5.jar", "projectC-1.7.jar"]
+        "a->unknown(*)"         | ["projectB-a-1.5.jar", "projectB-b-1.5.jar", "projectB-c-1.5.jar", "projectC-1.7.jar", "projectD-1.7.jar"]
+        "a->a(*),b(*);b->b(*)"  | ["projectB-a-1.5.jar", "projectB-b-1.5.jar", "projectC-1.7.jar", "projectD-1.7.jar"]
+    }
+
+    def "prefers revConstraint over rev when dynamic resolve mode is used"() {
+        given:
+        buildFile << """
+configurations {
+    compile
+}
+dependencies {
+    repositories {
+        ivy {
+            url "${ivyRepo.uri}"
+            resolve.dynamicMode = project.hasProperty('useDynamicResolve')
+        }
+    }
+    compile 'org:projectA:1.2'
+}
+task retrieve(type: Sync) {
+  from configurations.compile
+  into 'libs'
+}
+"""
+        ivyRepo.module('org', 'projectA', '1.2')
+                .dependsOn(organisation: 'org', module: 'projectB', revision: '1.5', revConstraint: '1.6')
+                .dependsOn(organisation: 'org', module: 'projectC', revision: 'alpha-12')
+                .publish()
+
+        ivyRepo.module('org', 'projectB', '1.5')
+                .publish()
+
+        ivyRepo.module('org', 'projectB', '1.6')
+                .publish()
+
+        ivyRepo.module('org', 'projectC', 'alpha-12')
+                .publish()
+
+        when:
+        executer.withArguments("-PuseDynamicResolve=true")
+        run 'retrieve'
+
+        then:
+        file('libs').assertHasDescendants('projectA-1.2.jar', 'projectB-1.6.jar', 'projectC-alpha-12.jar')
+
+        when:
+        run 'retrieve'
+
+        then:
+        file('libs').assertHasDescendants('projectA-1.2.jar', 'projectB-1.5.jar', 'projectC-alpha-12.jar')
+    }
+}
diff --git a/subprojects/core-impl/src/integTest/groovy/org/gradle/integtests/resolve/ivy/IvyResolveIntegrationTest.groovy b/subprojects/core-impl/src/integTest/groovy/org/gradle/integtests/resolve/ivy/IvyResolveIntegrationTest.groovy
index 5197a3b..a8e52f7 100644
--- a/subprojects/core-impl/src/integTest/groovy/org/gradle/integtests/resolve/ivy/IvyResolveIntegrationTest.groovy
+++ b/subprojects/core-impl/src/integTest/groovy/org/gradle/integtests/resolve/ivy/IvyResolveIntegrationTest.groovy
@@ -22,6 +22,7 @@ class IvyResolveIntegrationTest extends AbstractDependencyResolutionTest {
         given:
         ivyRepo.module("org.gradle", "test", "1.45")
                 .dependsOn("org.gradle", "other", "preview-1")
+                .artifact()
                 .artifact(classifier: "classifier")
                 .artifact(name: "test-extra")
                 .publish()
@@ -137,111 +138,4 @@ task check << {
         expect:
         succeeds "check"
     }
-
-    def "correctly handles wildcard configuration mapping in transitive dependencies"() {
-        given:
-        buildFile << """
-configurations {
-    compile
-}
-dependencies {
-    repositories {
-        ivy { url "${ivyRepo.uri}" }
-    }
-    compile 'ivy.configuration:projectA:1.2'
-}
-task retrieve(type: Sync) {
-  from configurations.compile
-  into 'libs'
-}
-"""
-        when: "projectA uses a wildcard configuration mapping for dependency on projectB"
-        def moduleA = ivyRepo.module('ivy.configuration', 'projectA', '1.2')
-                .configuration('parent')
-                .artifact([:])
-                .dependsOn(organisation: 'ivy.configuration', module: 'projectB', revision: '1.5', conf: 'runtime->*')
-                .publish()
-
-        ivyRepo.module('ivy.configuration', 'projectB', '1.5')
-                .configuration('child')
-                .artifact([name: 'projectB', conf: 'runtime'])
-                .artifact([name: 'projectB-child', conf: 'child'])
-                .dependsOn(organisation: 'ivy.configuration', module: 'projectC', revision: '1.7', conf: 'child->*')
-                .publish()
-
-        ivyRepo.module('ivy.configuration', 'projectC', '1.7').artifact([:]).publish()
-
-        and:
-        succeeds 'retrieve'
-
-        then: "artifacts and dependencies from all configurations of projectB are included"
-        file('libs').assertHasDescendants('projectA-1.2.jar', 'projectB-1.5.jar', 'projectB-child-1.5.jar', 'projectC-1.7.jar')
-
-        when: "projectB-1.5 is replaced by conflict resolution with projectB-1.6 that has a different set of configurations"
-
-        ivyRepo.module('ivy.configuration', 'projectB', '1.6')
-                .configuration('other')
-                .artifact([name: 'projectB-other', conf: 'other'])
-                .publish()
-
-        ivyRepo.module('ivy.configuration', 'projectD', '1.0')
-                .dependsOn('ivy.configuration', 'projectB', '1.6')
-                .publish()
-
-        moduleA.dependsOn('ivy.configuration', 'projectD', '1.0').publish()
-
-        and:
-        succeeds 'retrieve'
-
-        then: "we resolve artifacts from projectB-1.6 only"
-        file('libs').assertHasDescendants('projectA-1.2.jar', 'projectB-1.6.jar', 'projectB-other-1.6.jar', 'projectD-1.0.jar')
-    }
-
-    def "prefers revConstraint over rev when dynamic resolve mode is used"() {
-        given:
-        buildFile << """
-configurations {
-    compile
-}
-dependencies {
-    repositories {
-        ivy {
-            url "${ivyRepo.uri}"
-            resolve.dynamicMode = project.hasProperty('useDynamicResolve')
-        }
-    }
-    compile 'org:projectA:1.2'
-}
-task retrieve(type: Sync) {
-  from configurations.compile
-  into 'libs'
-}
-"""
-        ivyRepo.module('org', 'projectA', '1.2')
-                .dependsOn(organisation: 'org', module: 'projectB', revision: '1.5', revConstraint: '1.6')
-                .dependsOn(organisation: 'org', module: 'projectC', revision: 'alpha-12')
-                .publish()
-
-        ivyRepo.module('org', 'projectB', '1.5')
-                .publish()
-
-        ivyRepo.module('org', 'projectB', '1.6')
-                .publish()
-
-        ivyRepo.module('org', 'projectC', 'alpha-12')
-                .publish()
-
-        when:
-        executer.withArguments("-PuseDynamicResolve=true")
-        run 'retrieve'
-
-        then:
-        file('libs').assertHasDescendants('projectA-1.2.jar', 'projectB-1.6.jar', 'projectC-alpha-12.jar')
-
-        when:
-        run 'retrieve'
-
-        then:
-        file('libs').assertHasDescendants('projectA-1.2.jar', 'projectB-1.5.jar', 'projectC-alpha-12.jar')
-    }
 }
diff --git a/subprojects/core-impl/src/integTest/groovy/org/gradle/integtests/resolve/maven/BadPomFileResolveIntegrationTest.groovy b/subprojects/core-impl/src/integTest/groovy/org/gradle/integtests/resolve/maven/BadPomFileResolveIntegrationTest.groovy
index c871a5d..bf98795 100644
--- a/subprojects/core-impl/src/integTest/groovy/org/gradle/integtests/resolve/maven/BadPomFileResolveIntegrationTest.groovy
+++ b/subprojects/core-impl/src/integTest/groovy/org/gradle/integtests/resolve/maven/BadPomFileResolveIntegrationTest.groovy
@@ -16,16 +16,13 @@
 package org.gradle.integtests.resolve.maven
 
 import org.gradle.integtests.fixtures.AbstractDependencyResolutionTest
+import spock.lang.Ignore
 import spock.lang.Issue
 
 class BadPomFileResolveIntegrationTest extends AbstractDependencyResolutionTest {
-
     @Issue("http://issues.gradle.org/browse/GRADLE-1005")
     def "can handle self referencing dependency"() {
         given:
-        file("settings.gradle") << "include 'client'"
-
-        and:
         mavenRepo().module('group', 'artifact', '1.0').dependsOn('group', 'artifact', '1.0').publish()
 
         and:
@@ -43,4 +40,49 @@ class BadPomFileResolveIntegrationTest extends AbstractDependencyResolutionTest
         expect:
         succeeds ":libs"
     }
+
+    @Issue("http://issues.gradle.org/browse/GRADLE-2861")
+    @Ignore
+    def "can handle pom with placeholders in dependency management"() {
+        given:
+        server.start()
+
+        def parent = mavenHttpRepo.module('group', 'parent', '1.0').publish()
+        parent.pomFile.text = parent.pomFile.text.replace("</project>", """
+<dependencyManagement>
+    <dependencies>
+        <dependency>
+            <groupId>\${some.group}</groupId>
+            <artifactId>\${some.artifact}</artifactId>
+            <version>\${some.version}</version>
+        </dependency>
+    </dependencies>
+</dependencyManagement>
+</project>
+""")
+
+        def module = mavenHttpRepo.module('group', 'artifact', '1.0').parent('group', 'parent', '1.0').publish()
+
+        and:
+        buildFile << """
+            repositories {
+                maven { url "${mavenHttpRepo.uri}" }
+            }
+            configurations { compile }
+            dependencies {
+                compile "group:artifact:1.0"
+            }
+            task libs << { assert configurations.compile.files.collect {it.name} == ['artifact-1.0.jar'] }
+        """
+
+        and:
+        parent.pom.expectGet()
+        module.pom.expectGet()
+        module.artifact.expectGet()
+
+        expect:
+        // have to run twice to trigger the failure, to parse the descriptor from the cache
+        succeeds ":libs"
+        succeeds ":libs"
+    }
 }
\ No newline at end of file
diff --git a/subprojects/core-impl/src/integTest/groovy/org/gradle/integtests/resolve/maven/LegacyMavenRepoResolveIntegrationTest.groovy b/subprojects/core-impl/src/integTest/groovy/org/gradle/integtests/resolve/maven/LegacyMavenRepoResolveIntegrationTest.groovy
index 9d9470d..00ffd20 100644
--- a/subprojects/core-impl/src/integTest/groovy/org/gradle/integtests/resolve/maven/LegacyMavenRepoResolveIntegrationTest.groovy
+++ b/subprojects/core-impl/src/integTest/groovy/org/gradle/integtests/resolve/maven/LegacyMavenRepoResolveIntegrationTest.groovy
@@ -49,6 +49,7 @@ task check << {
         module.artifact.expectGet()
         module.artifact.sha1.expectGetMissing()
         module.artifact.md5.expectGet()
+        executer.withDeprecationChecksDisabled()
 
         expect:
         succeeds 'check'
@@ -68,6 +69,7 @@ task check << {
         module.artifact.expectGet()
         // TODO - shouldn't get checksum twice
         module.artifact.sha1.expectGet()
+        executer.withDeprecationChecksDisabled()
 
         then:
         executer.withArguments("--refresh-dependencies")
@@ -102,12 +104,17 @@ task check << {
         module.pom.sha1.expectGet()
         module.artifact.expectGet()
         module.artifact.sha1.expectGet()
+
         and:
         module.artifact.sha1.file.text = '1234'
+
+        and:
+        executer.withDeprecationChecksDisabled()
+
         expect:
         fails 'check'
         failureHasCause("Could not download artifact 'group:module:1.2 at jar'")
-        failureHasCause("invalid sha1: expected=1234 computed=5b253435f362abf1a12197966e332df7d2b153f5")
+        failureHasCause("invalid sha1: expected=1234 computed=2ee22701c9bd9af023ed20917897da5ce77336bc")
     }
 
     def "can configure resolver to fail when descriptor is not present"() {
@@ -137,6 +144,9 @@ task check << {
         and:
         module.pom.expectGetMissing()
 
+        and:
+        executer.withDeprecationChecksDisabled()
+
         expect:
         fails 'check'
         failureHasCause("Could not find group:module:1.2.")
@@ -171,6 +181,9 @@ task check << {
         module.artifact.expectHead()
         module.artifact.expectGet()
 
+        and:
+        executer.withDeprecationChecksDisabled()
+
         expect:
         succeeds "check"
     }
@@ -203,8 +216,10 @@ task check << {
         module.pom.expectGet()
         module.artifact.expectGet()
 
-        expect:
+        and:
         executer.withDeprecationChecksDisabled()
+
+        expect:
         succeeds "check"
     }
 }
diff --git a/subprojects/core-impl/src/integTest/groovy/org/gradle/integtests/resolve/maven/MavenBrokenRemoteResolveIntegrationTest.groovy b/subprojects/core-impl/src/integTest/groovy/org/gradle/integtests/resolve/maven/MavenBrokenRemoteResolveIntegrationTest.groovy
new file mode 100644
index 0000000..ba47d3f
--- /dev/null
+++ b/subprojects/core-impl/src/integTest/groovy/org/gradle/integtests/resolve/maven/MavenBrokenRemoteResolveIntegrationTest.groovy
@@ -0,0 +1,50 @@
+/*
+ * Copyright 2013 the original author or authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS 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.resolve.maven
+
+import org.gradle.integtests.fixtures.AbstractDependencyResolutionTest
+
+class MavenBrokenRemoteResolveIntegrationTest extends AbstractDependencyResolutionTest {
+    public void "reports POM that cannot be parsed"() {
+        server.start()
+        given:
+        buildFile << """
+repositories {
+    maven {
+        url "${mavenHttpRepo.uri}"
+    }
+}
+configurations { compile }
+dependencies {
+    compile 'group:projectA:1.2'
+}
+task showBroken << { println configurations.compile.files }
+"""
+
+        and:
+        def module = mavenHttpRepo.module('group', 'projectA', '1.2').publish()
+        module.pomFile.text = "<project>"
+
+        when:
+        module.pom.expectGet()
+
+        then:
+        fails "showBroken"
+        failure.assertResolutionFailure(":compile")
+            .assertHasCause("Could not parse POM ${module.pom.uri}")
+    }
+}
diff --git a/subprojects/core-impl/src/integTest/groovy/org/gradle/integtests/resolve/maven/MavenComponentMetadataRulesIntegrationTest.groovy b/subprojects/core-impl/src/integTest/groovy/org/gradle/integtests/resolve/maven/MavenComponentMetadataRulesIntegrationTest.groovy
new file mode 100644
index 0000000..d1047c4
--- /dev/null
+++ b/subprojects/core-impl/src/integTest/groovy/org/gradle/integtests/resolve/maven/MavenComponentMetadataRulesIntegrationTest.groovy
@@ -0,0 +1,61 @@
+/*
+ * Copyright 2013 the original author or authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS 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.resolve.maven
+
+import org.gradle.integtests.fixtures.AbstractDependencyResolutionTest
+
+class MavenComponentMetadataRulesIntegrationTest extends AbstractDependencyResolutionTest {
+    def "rules are provided with correct metadata"() {
+        given:
+        mavenRepo().module('group1', 'projectA', '1.0').publish()
+        mavenRepo().module('group2', 'projectB', '2.0-SNAPSHOT').publish()
+
+        and:
+        buildFile << """
+configurations { compile }
+repositories { maven { url "${mavenRepo().uri}" } }
+def allDetails = []
+dependencies {
+    compile 'group1:projectA:1.0'
+    compile 'group2:projectB:2.0-SNAPSHOT'
+    components {
+        eachComponent { allDetails << it }
+    }
+}
+
+task verify {
+    configurations.compile.resolve()
+
+    def projectA = allDetails.find { it.id.name == 'projectA' }
+    assert projectA != null
+    assert projectA.id.group == 'group1'
+    assert projectA.id.version == '1.0'
+    assert projectA.status == 'release'
+    assert projectA.statusScheme == ['integration', 'milestone', 'release']
+
+    def projectB = allDetails.find { it.id.name == 'projectB' }
+    assert projectB != null
+    assert projectB.id.group == 'group2'
+    assert projectB.id.version == '2.0-SNAPSHOT'
+    assert projectB.status == 'integration'
+    assert projectB.statusScheme == ['integration', 'milestone', 'release']
+}
+"""
+
+        expect:
+        succeeds 'verify'
+    }
+}
diff --git a/subprojects/core-impl/src/integTest/groovy/org/gradle/integtests/resolve/maven/MavenDependencyResolveIntegrationTest.groovy b/subprojects/core-impl/src/integTest/groovy/org/gradle/integtests/resolve/maven/MavenDependencyResolveIntegrationTest.groovy
index 0cc4101..ba8e950 100644
--- a/subprojects/core-impl/src/integTest/groovy/org/gradle/integtests/resolve/maven/MavenDependencyResolveIntegrationTest.groovy
+++ b/subprojects/core-impl/src/integTest/groovy/org/gradle/integtests/resolve/maven/MavenDependencyResolveIntegrationTest.groovy
@@ -16,6 +16,8 @@
 package org.gradle.integtests.resolve.maven
 
 import org.gradle.integtests.fixtures.AbstractDependencyResolutionTest
+import org.gradle.util.Requires
+import org.gradle.util.TestPrecondition
 
 class MavenDependencyResolveIntegrationTest extends AbstractDependencyResolutionTest {
     def "dependency includes main artifact and runtime dependencies of referenced module"() {
@@ -100,6 +102,7 @@ task check << {
         succeeds "check"
     }
 
+    @Requires(TestPrecondition.ONLINE)
     def "resolves dependencies on real projects"() {
         // Hibernate core brings in conflicts, exclusions and parent poms
         // Add a direct dependency on an earlier version of commons-collection than required by hibernate core
diff --git a/subprojects/core-impl/src/integTest/groovy/org/gradle/integtests/resolve/maven/MavenJcenterDependencyResolveIntegrationTest.groovy b/subprojects/core-impl/src/integTest/groovy/org/gradle/integtests/resolve/maven/MavenJcenterDependencyResolveIntegrationTest.groovy
new file mode 100644
index 0000000..ed554ca
--- /dev/null
+++ b/subprojects/core-impl/src/integTest/groovy/org/gradle/integtests/resolve/maven/MavenJcenterDependencyResolveIntegrationTest.groovy
@@ -0,0 +1,72 @@
+/*
+ * Copyright 2011 the original author or authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS 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.resolve.maven
+
+import org.gradle.integtests.fixtures.AbstractDependencyResolutionTest
+import org.gradle.util.Requires
+import org.gradle.util.TestPrecondition
+
+ at Requires(TestPrecondition.ONLINE)
+class MavenJcenterDependencyResolveIntegrationTest extends AbstractDependencyResolutionTest {
+    def "resolves a minimal dependency from bintray's jcenter"() {
+        given:
+        buildFile << """
+repositories {
+    jcenter()
+    jcenter { // just test this syntax works.
+        name = "otherJcenter"
+    }
+}
+
+configurations {
+    compile
+}
+
+dependencies {
+    compile "ch.qos.logback:logback-classic:1.0.13"
+}
+
+task check << {
+    def compile = configurations.compile
+    assert compile.resolvedConfiguration.firstLevelModuleDependencies.collect { it.name } == [
+        'ch.qos.logback:logback-classic:1.0.13',
+    ]
+
+    assert compile.collect { it.name } == [
+        'logback-classic-1.0.13.jar',
+        'logback-core-1.0.13.jar',
+        'slf4j-api-1.7.5.jar'
+    ]
+
+    assert compile.resolvedConfiguration.resolvedArtifacts.collect { it.file.name } == [
+        'logback-classic-1.0.13.jar',
+        'logback-core-1.0.13.jar',
+        'slf4j-api-1.7.5.jar'
+    ]
+}
+
+task repoNames << {
+    println repositories*.name
+}
+"""
+
+        expect:
+        succeeds "check", "repoNames"
+
+        and:
+        output.contains(["BintrayJCenter", "otherJcenter"].toString())
+    }
+}
\ No newline at end of file
diff --git a/subprojects/core-impl/src/integTest/groovy/org/gradle/integtests/resolve/maven/MavenLatestResolveIntegrationTest.groovy b/subprojects/core-impl/src/integTest/groovy/org/gradle/integtests/resolve/maven/MavenLatestResolveIntegrationTest.groovy
new file mode 100644
index 0000000..61e3017
--- /dev/null
+++ b/subprojects/core-impl/src/integTest/groovy/org/gradle/integtests/resolve/maven/MavenLatestResolveIntegrationTest.groovy
@@ -0,0 +1,91 @@
+/*
+ * Copyright 2012 the original author or authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS 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.resolve.maven
+
+import org.gradle.integtests.fixtures.AbstractDependencyResolutionTest
+
+class MavenLatestResolveIntegrationTest extends AbstractDependencyResolutionTest {
+    def "latest selector works correctly when (only) release versions are present"() {
+        given:
+        mavenRepo().module('group', 'projectA', '1.0').publish()
+        def highest = mavenRepo().module('group', 'projectA', '2.2').publish()
+        mavenRepo().module('group', 'projectA', '1.4').publish()
+
+        and:
+        buildFile << """
+configurations { compile }
+repositories { maven { url "${mavenRepo().uri}" } }
+dependencies { compile 'group:projectA:latest.$status' }
+task retrieve(type: Sync) {
+    from configurations.compile
+    into 'build'
+}
+"""
+
+        when:
+        run 'retrieve'
+
+        then:
+        def buildDir = file('build')
+        buildDir.assertHasDescendants(highest.artifactFile.name)
+
+        where:
+        status << ["integration", "milestone", "release"]
+    }
+
+    def "latest selector with unknown status leads to failure"() {
+        mavenRepo().module('group', 'projectA', '1.0').publish()
+
+        buildFile << """
+configurations { compile }
+repositories { maven { url "${mavenRepo().uri}" } }
+dependencies { compile 'group:projectA:latest.foo' }
+task retrieve(type: Sync) {
+    from configurations.compile
+    into 'build'
+}
+"""
+
+        expect:
+        fails 'retrieve'
+        // would be better if metadata validation failed (status not contained in status scheme)
+        failure.assertHasCause("Could not find any version that matches group:projectA:latest.foo.")
+    }
+
+    // describes the actual (not the desired) behavior
+    def "latest selector doesn't work correctly if highest version is snapshot"() {
+        given:
+        def moduleA = mavenRepo().module('group', 'projectA', '1.0').publish()
+        def moduleAA = mavenRepo().module('group', 'projectA', '1.2-SNAPSHOT').publish()
+
+        and:
+        buildFile << """
+configurations { compile }
+repositories { maven { url "${mavenRepo().uri}" } }
+dependencies { compile 'group:projectA:latest.release' }
+task retrieve(type: Sync) {
+    from configurations.compile
+    into 'build'
+}
+"""
+
+        when:
+        runAndFail("retrieve")
+
+        then:
+        failure.assertHasCause("Could not find any version that matches group:projectA:latest.release")
+    }
+}
diff --git a/subprojects/core-impl/src/integTest/groovy/org/gradle/integtests/resolve/maven/MavenParentPomResolveIntegrationTest.groovy b/subprojects/core-impl/src/integTest/groovy/org/gradle/integtests/resolve/maven/MavenParentPomResolveIntegrationTest.groovy
index 4e6841c..daeb983 100644
--- a/subprojects/core-impl/src/integTest/groovy/org/gradle/integtests/resolve/maven/MavenParentPomResolveIntegrationTest.groovy
+++ b/subprojects/core-impl/src/integTest/groovy/org/gradle/integtests/resolve/maven/MavenParentPomResolveIntegrationTest.groovy
@@ -69,6 +69,16 @@ task retrieve(type: Sync) {
 
         then:
         file('libs').assertHasDescendants('child-1.0.jar', 'parent_dep-1.2.jar', 'child_dep-1.7.jar')
+
+        when:
+        server.resetExpectations()
+        file('libs').deleteDir()
+
+        and:
+        run 'retrieve'
+
+        then:
+        file('libs').assertHasDescendants('child-1.0.jar', 'parent_dep-1.2.jar', 'child_dep-1.7.jar')
     }
 
     @Issue("GRADLE-2641")
@@ -160,4 +170,79 @@ task retrieve(type: Sync) {
         then:
         file('libs').assertHasDescendants('child-1.0.jar')
     }
+
+    def "uses cached parent pom located in a different repository"() {
+        given:
+        server.start()
+
+        def repo1 = mavenHttpRepo("repo1")
+        def repo2 = mavenHttpRepo("repo2")
+
+        // Parent not found in repo1
+        def missingParent = repo1.module("org", "parent")
+        def parent = repo2.module("org", "parent", "1.0")
+        parent.dependsOn("org", "parent_dep", "1.2")
+                .hasPackaging('pom')
+                .publish()
+
+        def parentDep = repo1.module("org", "parent_dep", "1.2").publish()
+
+        def child1 = repo1.module("org", "child1", "1.0")
+        child1.parent("org", "parent", "1.0").publish()
+        def child2 = repo1.module("org", "child2", "1.0")
+        child2.parent("org", "parent", "1.0").publish()
+
+        buildFile << """
+repositories {
+    maven { url '${repo1.uri}' }
+    maven { url '${repo2.uri}' }
+}
+configurations {
+    child1
+    child2
+}
+dependencies {
+    child1 'org:child1:1.0'
+    child2 'org:child2:1.0'
+}
+task retrieveChild1(type: Sync) {
+    into 'libs/child1'
+    from configurations.child1
+}
+task retrieveChild2(type: Sync) {
+    into 'libs/child2'
+    from configurations.child2
+}
+"""
+
+        when:
+        child1.pom.expectGet()
+        missingParent.pom.expectGetMissing()
+        missingParent.artifact.expectHeadMissing()
+        parent.pom.expectGet()
+        parent.artifact.expectHeadMissing()
+
+        child1.artifact.expectGet()
+
+        parentDep.pom.expectGet()
+        parentDep.artifact.expectGet()
+
+
+        and:
+        run 'retrieveChild1'
+
+        then:
+        file('libs/child1').assertHasDescendants('child1-1.0.jar', 'parent_dep-1.2.jar')
+
+        when:
+        server.resetExpectations()
+        child2.pom.expectGet()
+        child2.artifact.expectGet()
+
+        and:
+        run 'retrieveChild2'
+
+        then:
+        file('libs/child2').assertHasDescendants('child2-1.0.jar', 'parent_dep-1.2.jar')
+    }
 }
\ No newline at end of file
diff --git a/subprojects/core-impl/src/integTest/groovy/org/gradle/integtests/resolve/maven/MavenSnapshotResolveIntegrationTest.groovy b/subprojects/core-impl/src/integTest/groovy/org/gradle/integtests/resolve/maven/MavenSnapshotResolveIntegrationTest.groovy
index 6784b20..d3251fe 100644
--- a/subprojects/core-impl/src/integTest/groovy/org/gradle/integtests/resolve/maven/MavenSnapshotResolveIntegrationTest.groovy
+++ b/subprojects/core-impl/src/integTest/groovy/org/gradle/integtests/resolve/maven/MavenSnapshotResolveIntegrationTest.groovy
@@ -17,6 +17,7 @@ package org.gradle.integtests.resolve.maven
 
 import org.gradle.integtests.fixtures.AbstractDependencyResolutionTest
 import org.gradle.test.fixtures.maven.MavenHttpModule
+import spock.lang.Ignore
 
 class MavenSnapshotResolveIntegrationTest extends AbstractDependencyResolutionTest {
 
@@ -348,6 +349,10 @@ allprojects {
         from configurations.compile
     }
 }
+
+//imposing an artificial order so that the parallel build retrieves sequentially, GRADLE-2788
+retrieve.dependsOn ":a:retrieve"
+tasks.getByPath(":a:retrieve").dependsOn ":b:retrieve"
 """
         when: "Module is requested once"
         expectModuleServed(module)
@@ -361,6 +366,7 @@ allprojects {
         file('b/build').assertHasDescendants('testproject-1.0-SNAPSHOT.jar')
     }
 
+    @Ignore //TODO SF need to rework this test. First step might be turning off in-memory metadata caching for this test.
     def "can update snapshot artifact during build even if it is locked earlier in build"() {
         server.start()
         given:
@@ -517,10 +523,9 @@ project('second') {
         module.pom.expectHead()
         module.pom.sha1.expectGet()
         module.pom.expectGet()
-        def artifact = module.artifact
-        artifact.expectHead()
-        artifact.sha1.expectGet()
-        artifact.expectGet()
+        module.artifact.expectHead()
+        module.artifact.sha1.expectGet()
+        module.artifact.expectGet()
     }
 
     private expectChangedArtifactServed(MavenHttpModule module) {
diff --git a/subprojects/core-impl/src/integTest/resources/org/gradle/integtests/resolve/ArtifactDependenciesIntegrationTest/canNestModules/projectWithNestedModules.gradle b/subprojects/core-impl/src/integTest/resources/org/gradle/integtests/resolve/ArtifactDependenciesIntegrationTest/canNestModules/projectWithNestedModules.gradle
index d104d48..df5cea3 100644
--- a/subprojects/core-impl/src/integTest/resources/org/gradle/integtests/resolve/ArtifactDependenciesIntegrationTest/canNestModules/projectWithNestedModules.gradle
+++ b/subprojects/core-impl/src/integTest/resources/org/gradle/integtests/resolve/ArtifactDependenciesIntegrationTest/canNestModules/projectWithNestedModules.gradle
@@ -22,5 +22,5 @@ file("projectC-2.0.jar").text = ''
 
 task listJars << {
     List jars = configurations.compile.collect { it.name }
-    assert jars == ['projectA-1.2.jar', 'projectC-2.0.jar', 'projectB-1.5.jar']
+    assert jars == ['projectA-1.2.jar', 'projectB-1.5.jar', 'projectC-2.0.jar']
 }
diff --git a/subprojects/core-impl/src/integTest/resources/org/gradle/integtests/resolve/ArtifactDependenciesIntegrationTest/dependencyReportWithConflicts/projectWithConflicts.gradle b/subprojects/core-impl/src/integTest/resources/org/gradle/integtests/resolve/ArtifactDependenciesIntegrationTest/dependencyReportWithConflicts/projectWithConflicts.gradle
index 0deaf43..47b508d 100644
--- a/subprojects/core-impl/src/integTest/resources/org/gradle/integtests/resolve/ArtifactDependenciesIntegrationTest/dependencyReportWithConflicts/projectWithConflicts.gradle
+++ b/subprojects/core-impl/src/integTest/resources/org/gradle/integtests/resolve/ArtifactDependenciesIntegrationTest/dependencyReportWithConflicts/projectWithConflicts.gradle
@@ -3,7 +3,7 @@ allprojects {
         evictedTransitive
         evictedDirect
         multiProject
-        add('default')
+        create('default')
     }
     dependencies {
         repositories {
diff --git a/subprojects/core-impl/src/main/groovy/org/gradle/api/internal/artifacts/ArtifactDependencyResolver.java b/subprojects/core-impl/src/main/groovy/org/gradle/api/internal/artifacts/ArtifactDependencyResolver.java
index 7b5dae9..b94fd63 100644
--- a/subprojects/core-impl/src/main/groovy/org/gradle/api/internal/artifacts/ArtifactDependencyResolver.java
+++ b/subprojects/core-impl/src/main/groovy/org/gradle/api/internal/artifacts/ArtifactDependencyResolver.java
@@ -21,9 +21,6 @@ import org.gradle.api.internal.artifacts.repositories.ResolutionAwareRepository;
 
 import java.util.List;
 
-/**
- * @author Hans Dockter
- */
 public interface ArtifactDependencyResolver {
     ResolverResults resolve(ConfigurationInternal configuration, List<? extends ResolutionAwareRepository> repositories) throws ResolveException;
 }
diff --git a/subprojects/core-impl/src/main/groovy/org/gradle/api/internal/artifacts/BuildableModuleVersionPublishMetaData.java b/subprojects/core-impl/src/main/groovy/org/gradle/api/internal/artifacts/BuildableModuleVersionPublishMetaData.java
new file mode 100644
index 0000000..093fcad
--- /dev/null
+++ b/subprojects/core-impl/src/main/groovy/org/gradle/api/internal/artifacts/BuildableModuleVersionPublishMetaData.java
@@ -0,0 +1,28 @@
+/*
+ * Copyright 2013 the original author or authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY 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.apache.ivy.core.module.descriptor.Artifact;
+import org.apache.ivy.core.module.descriptor.DefaultModuleDescriptor;
+
+import java.io.File;
+
+public interface BuildableModuleVersionPublishMetaData extends ModuleVersionPublishMetaData {
+    DefaultModuleDescriptor getModuleDescriptor();
+
+    void addArtifact(Artifact artifact, File file);
+}
diff --git a/subprojects/core-impl/src/main/groovy/org/gradle/api/internal/artifacts/ConfigurationResolver.java b/subprojects/core-impl/src/main/groovy/org/gradle/api/internal/artifacts/ConfigurationResolver.java
index 501959b..0751e90 100644
--- a/subprojects/core-impl/src/main/groovy/org/gradle/api/internal/artifacts/ConfigurationResolver.java
+++ b/subprojects/core-impl/src/main/groovy/org/gradle/api/internal/artifacts/ConfigurationResolver.java
@@ -18,9 +18,6 @@ package org.gradle.api.internal.artifacts;
 import org.gradle.api.artifacts.ResolveException;
 import org.gradle.api.internal.artifacts.configurations.ConfigurationInternal;
 
-/**
- * @author Hans Dockter
- */
 public interface ConfigurationResolver {
     ResolverResults resolve(ConfigurationInternal configuration) throws ResolveException;
 }
diff --git a/subprojects/core-impl/src/main/groovy/org/gradle/api/internal/artifacts/DefaultArtifactIdentifier.java b/subprojects/core-impl/src/main/groovy/org/gradle/api/internal/artifacts/DefaultArtifactIdentifier.java
new file mode 100644
index 0000000..8f91951
--- /dev/null
+++ b/subprojects/core-impl/src/main/groovy/org/gradle/api/internal/artifacts/DefaultArtifactIdentifier.java
@@ -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.api.internal.artifacts;
+
+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.artifacts.ArtifactIdentifier;
+import org.gradle.api.artifacts.ModuleVersionIdentifier;
+
+import java.util.Collections;
+import java.util.Map;
+
+import static org.gradle.api.internal.artifacts.DefaultModuleVersionIdentifier.newId;
+
+public class DefaultArtifactIdentifier implements ArtifactIdentifier {
+    private final ModuleVersionIdentifier moduleVersionIdentifier;
+    private final String name;
+    private final String type;
+    private final String extension;
+    private final String classifier;
+
+    public DefaultArtifactIdentifier(ModuleVersionIdentifier moduleVersionIdentifier, String name, String type, String extension, String classifier) {
+        this.moduleVersionIdentifier = moduleVersionIdentifier;
+        this.name = name;
+        this.type = type;
+        this.extension = extension;
+        this.classifier = classifier;
+    }
+
+    public DefaultArtifactIdentifier(Artifact artifact) {
+        this(newId(artifact.getModuleRevisionId()), artifact.getName(), artifact.getType(), artifact.getExt(), artifact.getExtraAttribute("classifier"));
+    }
+
+    public static Artifact toArtifact(ArtifactIdentifier artifactIdentifier) {
+        ModuleVersionIdentifier moduleVersionIdentifier = artifactIdentifier.getModuleVersionIdentifier();
+        Map<String, String> extraAttributes = artifactIdentifier.getClassifier() == null ? Collections.<String, String>emptyMap() : Collections.singletonMap("classifier", artifactIdentifier.getClassifier());
+        ModuleRevisionId moduleRevisionId = ModuleRevisionId.newInstance(moduleVersionIdentifier.getGroup(), moduleVersionIdentifier.getName(), moduleVersionIdentifier.getVersion());
+        return new DefaultArtifact(moduleRevisionId, null, artifactIdentifier.getName(), artifactIdentifier.getType(), artifactIdentifier.getExtension(), extraAttributes);
+    }
+
+    public ModuleVersionIdentifier getModuleVersionIdentifier() {
+        return moduleVersionIdentifier;
+    }
+
+    public String getName() {
+        return name;
+    }
+
+    public String getType() {
+        return type;
+    }
+
+    public String getExtension() {
+        return extension;
+    }
+
+    public String getClassifier() {
+        return classifier;
+    }
+
+    @Override
+    public String toString() {
+        return String.format("module: %s, name: %s, ext: %s, classifier: %s", moduleVersionIdentifier, name, extension, classifier);
+    }
+
+    @Override
+    public boolean equals(Object o) {
+        if (this == o) {
+            return true;
+        }
+        if (!(o instanceof DefaultArtifactIdentifier)) {
+            return false;
+        }
+
+        DefaultArtifactIdentifier that = (DefaultArtifactIdentifier) o;
+
+        if (classifier != null ? !classifier.equals(that.classifier) : that.classifier != null) {
+            return false;
+        }
+        if (extension != null ? !extension.equals(that.extension) : that.extension != null) {
+            return false;
+        }
+        if (moduleVersionIdentifier != null ? !moduleVersionIdentifier.equals(that.moduleVersionIdentifier) : that.moduleVersionIdentifier != null) {
+            return false;
+        }
+        if (name != null ? !name.equals(that.name) : that.name != null) {
+            return false;
+        }
+        if (type != null ? !type.equals(that.type) : that.type != null) {
+            return false;
+        }
+
+        return true;
+    }
+
+    @Override
+    public int hashCode() {
+        int result = moduleVersionIdentifier != null ? moduleVersionIdentifier.hashCode() : 0;
+        result = 31 * result + (name != null ? name.hashCode() : 0);
+        result = 31 * result + (type != null ? type.hashCode() : 0);
+        result = 31 * result + (extension != null ? extension.hashCode() : 0);
+        result = 31 * result + (classifier != null ? classifier.hashCode() : 0);
+        return result;
+    }
+}
diff --git a/subprojects/core-impl/src/main/groovy/org/gradle/api/internal/artifacts/DefaultDependencyFactory.java b/subprojects/core-impl/src/main/groovy/org/gradle/api/internal/artifacts/DefaultDependencyFactory.java
index 69e7831..e46766f 100644
--- a/subprojects/core-impl/src/main/groovy/org/gradle/api/internal/artifacts/DefaultDependencyFactory.java
+++ b/subprojects/core-impl/src/main/groovy/org/gradle/api/internal/artifacts/DefaultDependencyFactory.java
@@ -28,9 +28,6 @@ import org.gradle.api.internal.notations.api.NotationParser;
 
 import java.util.Map;
 
-/**
- * @author Hans Dockter
- */
 public class DefaultDependencyFactory implements DependencyFactory {
     private final NotationParser<Dependency> dependencyNotationParser;
     private final NotationParser<ClientModule> clientModuleNotationParser;
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
index 59e2b1a..45d1241 100644
--- 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
@@ -28,6 +28,7 @@ import org.gradle.api.internal.artifacts.configurations.DefaultConfigurationCont
 import org.gradle.api.internal.artifacts.configurations.DependencyMetaDataProvider;
 import org.gradle.api.internal.artifacts.dependencies.DefaultExternalModuleDependency;
 import org.gradle.api.internal.artifacts.dsl.DefaultArtifactHandler;
+import org.gradle.api.internal.artifacts.dsl.DefaultComponentMetadataHandler;
 import org.gradle.api.internal.artifacts.dsl.DefaultRepositoryHandler;
 import org.gradle.api.internal.artifacts.dsl.PublishArtifactNotationParserFactory;
 import org.gradle.api.internal.artifacts.dsl.dependencies.DefaultDependencyHandler;
@@ -38,23 +39,30 @@ import org.gradle.api.internal.artifacts.ivyservice.dynamicversions.ModuleResolu
 import org.gradle.api.internal.artifacts.ivyservice.dynamicversions.SingleFileBackedModuleResolutionCache;
 import org.gradle.api.internal.artifacts.ivyservice.ivyresolve.ResolveIvyFactory;
 import org.gradle.api.internal.artifacts.ivyservice.ivyresolve.StartParameterResolutionOverride;
+import org.gradle.api.internal.artifacts.ivyservice.ivyresolve.memcache.InMemoryDependencyMetadataCache;
+import org.gradle.api.internal.artifacts.ivyservice.ivyresolve.parser.DefaultMetaDataParser;
+import org.gradle.api.internal.artifacts.ivyservice.ivyresolve.parser.ParserRegistry;
 import org.gradle.api.internal.artifacts.ivyservice.modulecache.DefaultModuleDescriptorCache;
 import org.gradle.api.internal.artifacts.ivyservice.modulecache.ModuleDescriptorCache;
 import org.gradle.api.internal.artifacts.ivyservice.moduleconverter.*;
 import org.gradle.api.internal.artifacts.ivyservice.moduleconverter.dependencies.*;
 import org.gradle.api.internal.artifacts.ivyservice.projectmodule.DefaultProjectModuleRegistry;
 import org.gradle.api.internal.artifacts.ivyservice.resolveengine.DefaultDependencyResolver;
+import org.gradle.api.internal.artifacts.ivyservice.resolveengine.store.ResolutionResultsStoreFactory;
 import org.gradle.api.internal.artifacts.mvnsettings.*;
 import org.gradle.api.internal.artifacts.repositories.DefaultBaseRepositoryFactory;
-import org.gradle.api.internal.artifacts.repositories.cachemanager.DownloadingRepositoryCacheManager;
-import org.gradle.api.internal.artifacts.repositories.cachemanager.LocalFileRepositoryCacheManager;
+import org.gradle.api.internal.artifacts.repositories.cachemanager.DownloadingRepositoryArtifactCache;
+import org.gradle.api.internal.artifacts.repositories.legacy.CustomIvyResolverRepositoryFactory;
+import org.gradle.api.internal.artifacts.repositories.legacy.DownloadingRepositoryCacheManager;
+import org.gradle.api.internal.artifacts.repositories.cachemanager.LocalFileRepositoryArtifactCache;
+import org.gradle.api.internal.artifacts.repositories.legacy.LegacyDependencyResolverRepositoryFactory;
+import org.gradle.api.internal.artifacts.repositories.legacy.LocalFileRepositoryCacheManager;
 import org.gradle.api.internal.artifacts.repositories.transport.RepositoryTransportFactory;
 import org.gradle.api.internal.externalresource.cached.ByUrlCachedExternalResourceIndex;
 import org.gradle.api.internal.externalresource.ivy.ArtifactAtRepositoryCachedArtifactIndex;
 import org.gradle.api.internal.externalresource.local.LocallyAvailableResourceFinder;
 import org.gradle.api.internal.externalresource.local.ivy.LocallyAvailableResourceFinderFactory;
 import org.gradle.api.internal.file.FileResolver;
-import org.gradle.api.internal.file.IdentityFileResolver;
 import org.gradle.api.internal.file.TmpDirTemporaryFileProvider;
 import org.gradle.api.internal.filestore.PathKeyFileStore;
 import org.gradle.api.internal.filestore.UniquePathKeyFileStore;
@@ -85,10 +93,13 @@ public class DefaultDependencyManagementServices extends DefaultServiceRegistry
         return new DefaultDependencyResolutionServices(this, resolver, dependencyMetaDataProvider, projectFinder, domainObjectContext);
     }
 
+    protected InMemoryDependencyMetadataCache createInMemoryDependencyMetadataCache() {
+        return new InMemoryDependencyMetadataCache();
+    }
+
     protected ResolveModuleDescriptorConverter createResolveModuleDescriptorConverter() {
         return new ResolveModuleDescriptorConverter(
                 get(ModuleDescriptorFactory.class),
-                get(DependencyDescriptorFactory.class),
                 get(ConfigurationsToModuleDescriptorConverter.class),
                 new DefaultDependenciesToModuleDescriptorConverter(
                         get(DependencyDescriptorFactory.class),
@@ -99,11 +110,11 @@ public class DefaultDependencyManagementServices extends DefaultServiceRegistry
     protected PublishModuleDescriptorConverter createPublishModuleDescriptorConverter() {
         return new PublishModuleDescriptorConverter(
                 get(ResolveModuleDescriptorConverter.class),
-                new DefaultArtifactsToModuleDescriptorConverter(DefaultArtifactsToModuleDescriptorConverter.RESOLVE_STRATEGY));
+                new DefaultArtifactsToModuleDescriptorConverter());
     }
 
     protected ModuleDescriptorFactory createModuleDescriptorFactory() {
-        return new DefaultModuleDescriptorFactory(get(IvyFactory.class), get(SettingsConverter.class));
+        return new DefaultModuleDescriptorFactory();
     }
 
     protected ExcludeRuleConverter createExcludeRuleConverter() {
@@ -150,7 +161,7 @@ public class DefaultDependencyManagementServices extends DefaultServiceRegistry
                 moduleMapParser,
                 selfResolvingDependencyFactory,
                 projParser,
-                new DependencyClassPathNotationParser(instantiator, get(ClassPathRegistry.class), new IdentityFileResolver()));
+                new DependencyClassPathNotationParser(instantiator, get(ClassPathRegistry.class), get(FileResolver.class).withNoBaseDir()));
 
         return new DefaultDependencyFactory(
                 new DependencyNotationParser(notationParsers),
@@ -207,16 +218,8 @@ public class DefaultDependencyManagementServices extends DefaultServiceRegistry
         return new ArtifactRevisionIdFileStore(get(PathKeyFileStore.class), new TmpDirTemporaryFileProvider());
     }
 
-    protected SettingsConverter createSettingsConverter() {
-        return new DefaultSettingsConverter(
-                new IvySettingsFactory(
-                        get(ArtifactCacheMetaData.class)
-                )
-        );
-    }
-
-    protected IvyFactory createIvyFactory() {
-        return new DefaultIvyFactory();
+    protected IvyContextManager createIvyContextManager() {
+        return new DefaultIvyContextManager();
     }
 
     protected MavenSettingsProvider createMavenSettingsProvider() {
@@ -234,20 +237,33 @@ public class DefaultDependencyManagementServices extends DefaultServiceRegistry
         return finderFactory.create();
     }
 
-    protected LocalFileRepositoryCacheManager createLocalRepositoryCacheManager() {
-        return new LocalFileRepositoryCacheManager("local");
+    protected LegacyDependencyResolverRepositoryFactory createCustomerResolverRepositoryFactory() {
+        return new CustomIvyResolverRepositoryFactory(
+                get(ProgressLoggerFactory.class),
+                new LocalFileRepositoryCacheManager("local"),
+                new DownloadingRepositoryCacheManager(
+                        "downloading",
+                        get(ArtifactRevisionIdFileStore.class),
+                        new TmpDirTemporaryFileProvider(),
+                        get(CacheLockingManager.class)
+                )
+        );
+    }
+
+    protected LocalFileRepositoryArtifactCache createLocalRepositoryArtifactCache() {
+        return new LocalFileRepositoryArtifactCache();
     }
 
-    protected DownloadingRepositoryCacheManager createDownloadingRepositoryCacheManager() {
-        return new DownloadingRepositoryCacheManager("downloading", get(ArtifactRevisionIdFileStore.class), get(ByUrlCachedExternalResourceIndex.class),
+    protected DownloadingRepositoryArtifactCache createDownloadingRepositoryArtifactCache() {
+        return new DownloadingRepositoryArtifactCache(get(ArtifactRevisionIdFileStore.class), get(ByUrlCachedExternalResourceIndex.class),
                 new TmpDirTemporaryFileProvider(), get(CacheLockingManager.class));
     }
 
     protected RepositoryTransportFactory createRepositoryTransportFactory() {
         return new RepositoryTransportFactory(
                 get(ProgressLoggerFactory.class),
-                get(LocalFileRepositoryCacheManager.class),
-                get(DownloadingRepositoryCacheManager.class),
+                get(LocalFileRepositoryArtifactCache.class),
+                get(DownloadingRepositoryArtifactCache.class),
                 new TmpDirTemporaryFileProvider(),
                 get(ByUrlCachedExternalResourceIndex.class)
         );
@@ -257,14 +273,13 @@ public class DefaultDependencyManagementServices extends DefaultServiceRegistry
         StartParameter startParameter = get(StartParameter.class);
         StartParameterResolutionOverride startParameterResolutionOverride = new StartParameterResolutionOverride(startParameter);
         return new ResolveIvyFactory(
-                get(IvyFactory.class),
-                get(SettingsConverter.class),
                 get(ModuleResolutionCache.class),
                 get(ModuleDescriptorCache.class),
                 get(ArtifactAtRepositoryCachedArtifactIndex.class),
                 get(CacheLockingManager.class),
                 startParameterResolutionOverride,
-                get(BuildCommencedTimeProvider.class));
+                get(BuildCommencedTimeProvider.class),
+                get(InMemoryDependencyMetadataCache.class));
     }
 
     protected ArtifactDependencyResolver createArtifactDependencyResolver() {
@@ -272,13 +287,14 @@ public class DefaultDependencyManagementServices extends DefaultServiceRegistry
                 get(ResolveIvyFactory.class),
                 get(PublishModuleDescriptorConverter.class),
                 new ResolvedArtifactFactory(
-                        get(CacheLockingManager.class)
+                        get(CacheLockingManager.class),
+                        get(IvyContextManager.class)
                 ),
                 new DefaultProjectModuleRegistry(
                         get(PublishModuleDescriptorConverter.class)),
-                get(ProjectAccessListener.class),
-                get(CacheLockingManager.class)
-        );
+                get(CacheLockingManager.class),
+                get(IvyContextManager.class),
+                get(ResolutionResultsStoreFactory.class));
         return new ErrorHandlingArtifactDependencyResolver(
                 new ShortcircuitEmptyConfigsArtifactDependencyResolver(
                         new SelfResolvingDependencyResolver(
@@ -287,6 +303,10 @@ public class DefaultDependencyManagementServices extends DefaultServiceRegistry
                                         resolver))));
     }
 
+    protected ResolutionResultsStoreFactory createResolutionResultsStoreFactory() {
+        return new ResolutionResultsStoreFactory(new TmpDirTemporaryFileProvider());
+    }
+
     private class DefaultDependencyResolutionServices implements DependencyResolutionServices {
         private final ServiceRegistry parent;
         private final FileResolver fileResolver;
@@ -296,6 +316,7 @@ public class DefaultDependencyManagementServices extends DefaultServiceRegistry
         private DefaultRepositoryHandler repositoryHandler;
         private ConfigurationContainerInternal configurationContainer;
         private DependencyHandler dependencyHandler;
+        private DefaultComponentMetadataHandler componentMetadataHandler;
         private DefaultArtifactHandler artifactHandler;
         private BaseRepositoryFactory baseRepositoryFactory;
 
@@ -324,9 +345,9 @@ public class DefaultDependencyManagementServices extends DefaultServiceRegistry
                         instantiator,
                         get(RepositoryTransportFactory.class),
                         get(LocallyAvailableResourceFinder.class),
-                        get(ProgressLoggerFactory.class),
-                        get(LocalFileRepositoryCacheManager.class),
-                        get(DownloadingRepositoryCacheManager.class)
+                        new DefaultMetaDataParser(new ParserRegistry()),
+                        getComponentMetadataHandler(),
+                        get(LegacyDependencyResolverRepositoryFactory.class)
                 );
             }
 
@@ -351,11 +372,19 @@ public class DefaultDependencyManagementServices extends DefaultServiceRegistry
 
         public DependencyHandler getDependencyHandler() {
             if (dependencyHandler == null) {
-                dependencyHandler = new DefaultDependencyHandler(getConfigurationContainer(), parent.get(DependencyFactory.class), projectFinder);
+                dependencyHandler = new DefaultDependencyHandler(getConfigurationContainer(), parent.get(DependencyFactory.class), projectFinder, getComponentMetadataHandler());
             }
             return dependencyHandler;
         }
 
+        public DefaultComponentMetadataHandler getComponentMetadataHandler() {
+            if (componentMetadataHandler == null) {
+                Instantiator instantiator = parent.get(Instantiator.class);
+                componentMetadataHandler = instantiator.newInstance(DefaultComponentMetadataHandler.class, instantiator);
+            }
+            return componentMetadataHandler;
+        }
+
         public ArtifactHandler getArtifactHandler() {
             if (artifactHandler == null) {
                 NotationParser<PublishArtifact> publishArtifactNotationParser = new PublishArtifactNotationParserFactory(get(Instantiator.class), dependencyMetaDataProvider).create();
@@ -376,13 +405,12 @@ public class DefaultDependencyManagementServices extends DefaultServiceRegistry
 
         ArtifactPublisher createArtifactPublisher() {
             return new IvyBackedArtifactPublisher(
-                    get(SettingsConverter.class),
                     get(PublishModuleDescriptorConverter.class),
-                    get(IvyFactory.class),
-                    new DefaultIvyDependencyPublisher()
+                    get(IvyContextManager.class),
+                    new DefaultIvyDependencyPublisher(),
+                    new IvyXmlModuleDescriptorWriter()
             );
         }
-
     }
 
     private static class DefaultArtifactPublicationServices implements ArtifactPublicationServices {
@@ -396,16 +424,6 @@ public class DefaultDependencyManagementServices extends DefaultServiceRegistry
             return dependencyResolutionServices.createRepositoryHandler();
         }
 
-        public ModuleDescriptorConverter getDescriptorFileModuleConverter() {
-            return new PublishModuleDescriptorConverter(
-                    dependencyResolutionServices.parent.get(ResolveModuleDescriptorConverter.class),
-                    new DefaultArtifactsToModuleDescriptorConverter(DefaultArtifactsToModuleDescriptorConverter.IVY_FILE_STRATEGY));
-        }
-
-        public IvyModuleDescriptorWriter getIvyModuleDescriptorWriter() {
-            return new IvyXmlModuleDescriptorWriter();
-        }
-
         public ArtifactPublisher createArtifactPublisher() {
             return dependencyResolutionServices.createArtifactPublisher();
         }
diff --git a/subprojects/core-impl/src/main/groovy/org/gradle/api/internal/artifacts/DefaultModuleVersionIdentifier.java b/subprojects/core-impl/src/main/groovy/org/gradle/api/internal/artifacts/DefaultModuleVersionIdentifier.java
index 346f2be..2ab0961 100755
--- a/subprojects/core-impl/src/main/groovy/org/gradle/api/internal/artifacts/DefaultModuleVersionIdentifier.java
+++ b/subprojects/core-impl/src/main/groovy/org/gradle/api/internal/artifacts/DefaultModuleVersionIdentifier.java
@@ -15,6 +15,7 @@
  */
 package org.gradle.api.internal.artifacts;
 
+import org.apache.ivy.core.module.id.ModuleRevisionId;
 import org.gradle.api.artifacts.Module;
 import org.gradle.api.artifacts.ModuleIdentifier;
 import org.gradle.api.artifacts.ModuleVersionIdentifier;
@@ -83,4 +84,8 @@ public class DefaultModuleVersionIdentifier implements ModuleVersionIdentifier {
     public static ModuleVersionIdentifier newId(String group, String name, String version) {
         return new DefaultModuleVersionIdentifier(group, name, version);
     }
+
+    public static ModuleVersionIdentifier newId(ModuleRevisionId moduleRevisionId) {
+        return new DefaultModuleVersionIdentifier(moduleRevisionId.getOrganisation(), moduleRevisionId.getName(), moduleRevisionId.getRevision());
+    }
 }
diff --git a/subprojects/core-impl/src/main/groovy/org/gradle/api/internal/artifacts/DefaultModuleVersionPublishMetaData.java b/subprojects/core-impl/src/main/groovy/org/gradle/api/internal/artifacts/DefaultModuleVersionPublishMetaData.java
new file mode 100644
index 0000000..307bf02
--- /dev/null
+++ b/subprojects/core-impl/src/main/groovy/org/gradle/api/internal/artifacts/DefaultModuleVersionPublishMetaData.java
@@ -0,0 +1,52 @@
+/*
+ * Copyright 2013 the original author or authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY 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.apache.ivy.core.module.descriptor.Artifact;
+import org.apache.ivy.core.module.descriptor.DefaultModuleDescriptor;
+import org.gradle.api.artifacts.ModuleVersionIdentifier;
+
+import java.io.File;
+import java.util.LinkedHashMap;
+import java.util.Map;
+
+public class DefaultModuleVersionPublishMetaData implements BuildableModuleVersionPublishMetaData {
+    private final Map<Artifact, File> artifacts = new LinkedHashMap<Artifact, File>();
+    private final DefaultModuleDescriptor moduleDescriptor;
+    private final ModuleVersionIdentifier id;
+
+    public DefaultModuleVersionPublishMetaData(DefaultModuleDescriptor moduleDescriptor) {
+        this.moduleDescriptor = moduleDescriptor;
+        id = DefaultModuleVersionIdentifier.newId(moduleDescriptor.getModuleRevisionId());
+    }
+
+    public ModuleVersionIdentifier getId() {
+        return id;
+    }
+
+    public DefaultModuleDescriptor getModuleDescriptor() {
+        return moduleDescriptor;
+    }
+
+    public void addArtifact(Artifact artifact, File file) {
+        artifacts.put(artifact, file);
+    }
+
+    public Map<Artifact, File> getArtifacts() {
+        return artifacts;
+    }
+}
diff --git a/subprojects/core-impl/src/main/groovy/org/gradle/api/internal/artifacts/DefaultModuleVersionSelector.java b/subprojects/core-impl/src/main/groovy/org/gradle/api/internal/artifacts/DefaultModuleVersionSelector.java
index 17af643..b285c6f 100644
--- a/subprojects/core-impl/src/main/groovy/org/gradle/api/internal/artifacts/DefaultModuleVersionSelector.java
+++ b/subprojects/core-impl/src/main/groovy/org/gradle/api/internal/artifacts/DefaultModuleVersionSelector.java
@@ -16,12 +16,10 @@
 
 package org.gradle.api.internal.artifacts;
 
+import org.apache.ivy.core.module.id.ModuleRevisionId;
 import org.gradle.api.artifacts.ModuleVersionIdentifier;
 import org.gradle.api.artifacts.ModuleVersionSelector;
 
-/**
- * by Szczepan Faber, created at: 11/13/11
- */
 public class DefaultModuleVersionSelector implements ModuleVersionSelector {
 
     private String group;
@@ -105,4 +103,8 @@ public class DefaultModuleVersionSelector implements ModuleVersionSelector {
     public static ModuleVersionSelector newSelector(String group, String name, String version) {
         return new DefaultModuleVersionSelector(group, name, version);
     }
+
+    public static ModuleVersionSelector newSelector(ModuleRevisionId module) {
+        return newSelector(module.getOrganisation(), module.getName(), module.getRevision());
+    }
 }
\ No newline at end of file
diff --git a/subprojects/core-impl/src/main/groovy/org/gradle/api/internal/artifacts/DefaultProjectDependencyFactory.java b/subprojects/core-impl/src/main/groovy/org/gradle/api/internal/artifacts/DefaultProjectDependencyFactory.java
index bf9301b..62af822 100644
--- a/subprojects/core-impl/src/main/groovy/org/gradle/api/internal/artifacts/DefaultProjectDependencyFactory.java
+++ b/subprojects/core-impl/src/main/groovy/org/gradle/api/internal/artifacts/DefaultProjectDependencyFactory.java
@@ -23,9 +23,6 @@ import org.gradle.api.internal.project.ProjectInternal;
 import org.gradle.initialization.ProjectAccessListener;
 import org.gradle.internal.reflect.Instantiator;
 
-/**
- * by Szczepan Faber, created at: 2/5/13
- */
 public class DefaultProjectDependencyFactory {
     private final ProjectAccessListener projectAccessListener;
     private final Instantiator instantiator;
diff --git a/subprojects/core-impl/src/main/groovy/org/gradle/api/internal/artifacts/DefaultResolvedArtifact.java b/subprojects/core-impl/src/main/groovy/org/gradle/api/internal/artifacts/DefaultResolvedArtifact.java
index 02cea03..b034e65 100644
--- a/subprojects/core-impl/src/main/groovy/org/gradle/api/internal/artifacts/DefaultResolvedArtifact.java
+++ b/subprojects/core-impl/src/main/groovy/org/gradle/api/internal/artifacts/DefaultResolvedArtifact.java
@@ -27,20 +27,21 @@ import java.io.File;
 import java.util.HashMap;
 import java.util.Map;
 
-/**
- * @author Hans Dockter
- */
 public class DefaultResolvedArtifact implements ResolvedArtifact {
-    private final ResolvedDependency resolvedDependency;
     private final Map<String, String> extraAttributes;
     private final String name;
     private final String type;
     private final String ext;
+    private final ResolvedModuleVersion owner;
+    private long id;
+    private final Factory<ResolvedDependency> ownerSource;
     private Factory<File> artifactSource;
     private File file;
 
-    public DefaultResolvedArtifact(ResolvedDependency resolvedDependency, Artifact artifact, Factory<File> artifactSource) {
-        this.resolvedDependency = resolvedDependency;
+    public DefaultResolvedArtifact(ResolvedModuleVersion owner, Factory<ResolvedDependency> ownerSource, Artifact artifact, Factory<File> artifactSource, long id) {
+        this.ownerSource = ownerSource;
+        this.owner = owner;
+        this.id = id;
         // Unpack the stuff that we're interested from the artifact and discard. The artifact instance drags in a whole pile of stuff that
         // we don't want to retain references to.
         this.name = artifact.getName();
@@ -50,21 +51,26 @@ public class DefaultResolvedArtifact implements ResolvedArtifact {
         this.artifactSource = artifactSource;
     }
 
+    public long getId() {
+        return id;
+    }
+
     public ResolvedDependency getResolvedDependency() {
         DeprecationLogger.nagUserOfDeprecated(
                 "ResolvedArtifact.getResolvedDependency()",
                 "For version info use ResolvedArtifact.getModuleVersion(), to access the dependency graph use ResolvedConfiguration.getFirstLevelModuleDependencies()"
         );
-        return resolvedDependency;
+        //resolvedDependency is expensive so lazily create it
+        return ownerSource.create();
     }
 
     public ResolvedModuleVersion getModuleVersion() {
-        return resolvedDependency.getModule();
+        return owner;
     }
 
     @Override
     public String toString() {
-        return String.format("[ResolvedArtifact dependency:%s name:%s classifier:%s extension:%s type:%s]", resolvedDependency, getName(), getClassifier(), getExtension(), getType());
+        return String.format("[ResolvedArtifact dependency:%s name:%s classifier:%s extension:%s type:%s]", owner, getName(), getClassifier(), getExtension(), getType());
     }
 
     @Override
@@ -76,7 +82,7 @@ public class DefaultResolvedArtifact implements ResolvedArtifact {
             return false;
         }
         DefaultResolvedArtifact other = (DefaultResolvedArtifact) obj;
-        if (!other.resolvedDependency.getModule().getId().equals(resolvedDependency.getModule().getId())) {
+        if (!other.owner.getId().equals(owner.getId())) {
             return false;
         }
         if (!other.getName().equals(getName())) {
@@ -96,7 +102,7 @@ public class DefaultResolvedArtifact implements ResolvedArtifact {
 
     @Override
     public int hashCode() {
-        return resolvedDependency.getModule().getId().hashCode() ^ getName().hashCode() ^ getType().hashCode() ^ getExtension().hashCode() ^ extraAttributes.hashCode();
+        return owner.getId().hashCode() ^ getName().hashCode() ^ getType().hashCode() ^ getExtension().hashCode() ^ extraAttributes.hashCode();
     }
 
     public String getName() {
diff --git a/subprojects/core-impl/src/main/groovy/org/gradle/api/internal/artifacts/DefaultResolvedDependency.java b/subprojects/core-impl/src/main/groovy/org/gradle/api/internal/artifacts/DefaultResolvedDependency.java
index 3c8d725..fd83484 100644
--- a/subprojects/core-impl/src/main/groovy/org/gradle/api/internal/artifacts/DefaultResolvedDependency.java
+++ b/subprojects/core-impl/src/main/groovy/org/gradle/api/internal/artifacts/DefaultResolvedDependency.java
@@ -25,9 +25,6 @@ import org.gradle.api.artifacts.ResolvedModuleVersion;
 
 import java.util.*;
 
-/**
- * @author Hans Dockter
- */
 public class DefaultResolvedDependency implements ResolvedDependency {
     private final Set<ResolvedDependency> children = new LinkedHashSet<ResolvedDependency>();
     private final Set<ResolvedDependency> parents = new LinkedHashSet<ResolvedDependency>();
@@ -38,16 +35,12 @@ public class DefaultResolvedDependency implements ResolvedDependency {
     private final Map<ResolvedDependency, Set<ResolvedArtifact>> allArtifactsCache = new HashMap<ResolvedDependency, Set<ResolvedArtifact>>();
     private Set<ResolvedArtifact> allModuleArtifactsCache;
 
-    public DefaultResolvedDependency(String name, String moduleGroup, String moduleName, String moduleVersion, String configuration) {
-        this.name = name;
-        id = new ResolvedConfigurationIdentifier(moduleGroup, moduleName, moduleVersion, configuration);
+    public DefaultResolvedDependency(ModuleVersionIdentifier moduleVersionIdentifier, String configuration) {
+        this.name = String.format("%s:%s:%s", moduleVersionIdentifier.getGroup(), moduleVersionIdentifier.getName(), moduleVersionIdentifier.getVersion());
+        id = new ResolvedConfigurationIdentifier(moduleVersionIdentifier, configuration);
         this.moduleArtifacts = new TreeSet<ResolvedArtifact>(new ResolvedArtifactComparator());
     }
 
-    public DefaultResolvedDependency(String moduleGroup, String moduleName, String moduleVersion, String configuration) {
-        this(moduleGroup + ":" + moduleName + ":" + moduleVersion, moduleGroup, moduleName, moduleVersion, configuration);
-    }
-
     public String getName() {
         return name;
     }
@@ -75,7 +68,7 @@ public class DefaultResolvedDependency implements ResolvedDependency {
     public ResolvedModuleVersion getModule() {
         return new ResolvedModuleVersion() {
             public ModuleVersionIdentifier getId() {
-                return new DefaultModuleVersionIdentifier(id.getModuleGroup(), id.getModuleName(), id.getModuleVersion());
+                return id.getId();
             }
         };
     }
@@ -102,7 +95,7 @@ public class DefaultResolvedDependency implements ResolvedDependency {
 
     public Set<ResolvedArtifact> getParentArtifacts(ResolvedDependency parent) {
         if (!parents.contains(parent)) {
-            throw new InvalidUserDataException("Unknown Parent");
+            throw new InvalidUserDataException("Provided dependency (" + parent + ") must be a parent of: " + this);
         }
         Set<ResolvedArtifact> artifacts = parentArtifacts.get(parent);
         return artifacts == null ? Collections.<ResolvedArtifact>emptySet() : artifacts;
diff --git a/subprojects/core-impl/src/main/groovy/org/gradle/api/internal/artifacts/ModuleMetadataProcessor.java b/subprojects/core-impl/src/main/groovy/org/gradle/api/internal/artifacts/ModuleMetadataProcessor.java
new file mode 100644
index 0000000..65975cc
--- /dev/null
+++ b/subprojects/core-impl/src/main/groovy/org/gradle/api/internal/artifacts/ModuleMetadataProcessor.java
@@ -0,0 +1,22 @@
+/*
+ * Copyright 2013 the original author or authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY 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.artifacts.ivyservice.ivyresolve.ModuleVersionMetaData;
+
+public interface ModuleMetadataProcessor {
+    void process(ModuleVersionMetaData metadata);
+}
diff --git a/subprojects/core-impl/src/main/groovy/org/gradle/api/internal/artifacts/ModuleVersionIdentifierSerializer.java b/subprojects/core-impl/src/main/groovy/org/gradle/api/internal/artifacts/ModuleVersionIdentifierSerializer.java
index f27db28..4cd8137 100644
--- a/subprojects/core-impl/src/main/groovy/org/gradle/api/internal/artifacts/ModuleVersionIdentifierSerializer.java
+++ b/subprojects/core-impl/src/main/groovy/org/gradle/api/internal/artifacts/ModuleVersionIdentifierSerializer.java
@@ -17,25 +17,23 @@
 package org.gradle.api.internal.artifacts;
 
 import org.gradle.api.artifacts.ModuleVersionIdentifier;
-import org.gradle.messaging.serialize.DataStreamBackedSerializer;
+import org.gradle.messaging.serialize.Decoder;
+import org.gradle.messaging.serialize.Encoder;
+import org.gradle.messaging.serialize.Serializer;
 
-import java.io.DataInput;
-import java.io.DataOutput;
 import java.io.IOException;
 
-public class ModuleVersionIdentifierSerializer extends DataStreamBackedSerializer<ModuleVersionIdentifier> {
-    @Override
-    public void write(DataOutput dataOutput, ModuleVersionIdentifier value) throws IOException {
-        dataOutput.writeUTF(value.getGroup());
-        dataOutput.writeUTF(value.getName());
-        dataOutput.writeUTF(value.getVersion());
+public class ModuleVersionIdentifierSerializer implements Serializer<ModuleVersionIdentifier> {
+    public void write(Encoder encoder, ModuleVersionIdentifier value) throws IOException {
+        encoder.writeString(value.getGroup());
+        encoder.writeString(value.getName());
+        encoder.writeString(value.getVersion());
     }
 
-    @Override
-    public ModuleVersionIdentifier read(DataInput dataInput) throws IOException {
-        String group = dataInput.readUTF();
-        String module = dataInput.readUTF();
-        String version = dataInput.readUTF();
+    public ModuleVersionIdentifier read(Decoder decoder) throws IOException {
+        String group = decoder.readString();
+        String module = decoder.readString();
+        String version = decoder.readString();
         return DefaultModuleVersionIdentifier.newId(group, module, version);
     }
 }
diff --git a/subprojects/core-impl/src/main/groovy/org/gradle/api/internal/artifacts/ModuleVersionSelectorSerializer.java b/subprojects/core-impl/src/main/groovy/org/gradle/api/internal/artifacts/ModuleVersionSelectorSerializer.java
new file mode 100644
index 0000000..8e43bcf
--- /dev/null
+++ b/subprojects/core-impl/src/main/groovy/org/gradle/api/internal/artifacts/ModuleVersionSelectorSerializer.java
@@ -0,0 +1,41 @@
+/*
+ * Copyright 2013 the original author or authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY 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.ModuleVersionSelector;
+import org.gradle.messaging.serialize.Decoder;
+import org.gradle.messaging.serialize.Encoder;
+import org.gradle.messaging.serialize.Serializer;
+
+import java.io.IOException;
+
+import static org.gradle.api.internal.artifacts.DefaultModuleVersionSelector.newSelector;
+
+public class ModuleVersionSelectorSerializer implements Serializer<ModuleVersionSelector> {
+    public ModuleVersionSelector read(Decoder decoder) throws IOException {
+        String group = decoder.readString();
+        String name = decoder.readString();
+        String version = decoder.readString();
+        return newSelector(group, name, version);
+    }
+
+    public void write(Encoder encoder, ModuleVersionSelector value) throws IOException {
+        encoder.writeString(value.getGroup());
+        encoder.writeString(value.getName());
+        encoder.writeString(value.getVersion());
+    }
+}
diff --git a/subprojects/core-impl/src/main/groovy/org/gradle/api/internal/artifacts/PlexusLoggerAdapter.java b/subprojects/core-impl/src/main/groovy/org/gradle/api/internal/artifacts/PlexusLoggerAdapter.java
index 4bb5c77..e02ab3b 100644
--- a/subprojects/core-impl/src/main/groovy/org/gradle/api/internal/artifacts/PlexusLoggerAdapter.java
+++ b/subprojects/core-impl/src/main/groovy/org/gradle/api/internal/artifacts/PlexusLoggerAdapter.java
@@ -17,9 +17,6 @@ package org.gradle.api.internal.artifacts;
 
 import org.codehaus.plexus.logging.Logger;
 
-/**
- * @author Hans Dockter
- */
 public class PlexusLoggerAdapter implements Logger {
     org.slf4j.Logger logger;
 
diff --git a/subprojects/core-impl/src/main/groovy/org/gradle/api/internal/artifacts/ResolvedConfigurationIdentifier.java b/subprojects/core-impl/src/main/groovy/org/gradle/api/internal/artifacts/ResolvedConfigurationIdentifier.java
index a6710a6..1fbdb6f 100644
--- a/subprojects/core-impl/src/main/groovy/org/gradle/api/internal/artifacts/ResolvedConfigurationIdentifier.java
+++ b/subprojects/core-impl/src/main/groovy/org/gradle/api/internal/artifacts/ResolvedConfigurationIdentifier.java
@@ -22,9 +22,9 @@ public class ResolvedConfigurationIdentifier {
     private final ModuleVersionIdentifier id;
     private final String configuration;
 
-    public ResolvedConfigurationIdentifier(String moduleGroup, String moduleName, String moduleVersion,
+    public ResolvedConfigurationIdentifier(ModuleVersionIdentifier moduleVersionIdentifier,
                                            String configuration) {
-        this.id = new DefaultModuleVersionIdentifier(moduleGroup, moduleName, moduleVersion);
+        this.id = moduleVersionIdentifier;
         this.configuration = configuration;
     }
 
@@ -50,7 +50,7 @@ public class ResolvedConfigurationIdentifier {
 
     @Override
     public String toString() {
-        return String.format("%s:%s:%s:%s", getModuleGroup(), getModuleVersion(), getModuleName(), configuration);
+        return String.format("%s:%s:%s:%s", getModuleGroup(), getModuleName(), getModuleVersion(), configuration);
     }
 
     @Override
diff --git a/subprojects/core-impl/src/main/groovy/org/gradle/api/internal/artifacts/ResolvedConfigurationIdentifierSerializer.java b/subprojects/core-impl/src/main/groovy/org/gradle/api/internal/artifacts/ResolvedConfigurationIdentifierSerializer.java
new file mode 100644
index 0000000..ed9accc
--- /dev/null
+++ b/subprojects/core-impl/src/main/groovy/org/gradle/api/internal/artifacts/ResolvedConfigurationIdentifierSerializer.java
@@ -0,0 +1,39 @@
+/*
+ * Copyright 2012 the original author or authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY 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.ModuleVersionIdentifier;
+import org.gradle.messaging.serialize.Decoder;
+import org.gradle.messaging.serialize.Encoder;
+import org.gradle.messaging.serialize.Serializer;
+
+import java.io.IOException;
+
+public class ResolvedConfigurationIdentifierSerializer implements Serializer<ResolvedConfigurationIdentifier> {
+    private final ModuleVersionIdentifierSerializer idSerializer = new ModuleVersionIdentifierSerializer();
+
+    public ResolvedConfigurationIdentifier read(Decoder decoder) throws IOException {
+        ModuleVersionIdentifier id = idSerializer.read(decoder);
+        String configuration = decoder.readString();
+        return new ResolvedConfigurationIdentifier(id, configuration);
+    }
+
+    public void write(Encoder encoder, ResolvedConfigurationIdentifier value) throws IOException {
+        idSerializer.write(encoder, value.getId());
+        encoder.writeString(value.getConfiguration());
+    }
+}
diff --git a/subprojects/core-impl/src/main/groovy/org/gradle/api/internal/artifacts/ResolverResults.java b/subprojects/core-impl/src/main/groovy/org/gradle/api/internal/artifacts/ResolverResults.java
index 6811040..4320f80 100644
--- a/subprojects/core-impl/src/main/groovy/org/gradle/api/internal/artifacts/ResolverResults.java
+++ b/subprojects/core-impl/src/main/groovy/org/gradle/api/internal/artifacts/ResolverResults.java
@@ -20,9 +20,6 @@ import org.gradle.api.artifacts.ResolveException;
 import org.gradle.api.artifacts.ResolvedConfiguration;
 import org.gradle.api.artifacts.result.ResolutionResult;
 
-/**
- * by Szczepan Faber, created at: 10/16/12
- */
 public class ResolverResults {
 
     private final ResolvedConfiguration resolvedConfiguration;
diff --git a/subprojects/core-impl/src/main/groovy/org/gradle/api/internal/artifacts/configurations/Configurations.java b/subprojects/core-impl/src/main/groovy/org/gradle/api/internal/artifacts/configurations/Configurations.java
index c92b018..793c227 100644
--- a/subprojects/core-impl/src/main/groovy/org/gradle/api/internal/artifacts/configurations/Configurations.java
+++ b/subprojects/core-impl/src/main/groovy/org/gradle/api/internal/artifacts/configurations/Configurations.java
@@ -21,9 +21,6 @@ import java.util.Collection;
 import java.util.HashSet;
 import java.util.Set;
 
-/**
- * @author Hans Dockter
- */
 public class Configurations {
     public static Set<String> getNames(Collection<? extends Configuration> configurations, boolean includeExtended) {
         Set<Configuration> allConfigurations = new HashSet<Configuration>(configurations);
@@ -49,15 +46,10 @@ public class Configurations {
         return allConfigurations;
     }
 
-    public static String uploadInternalTaskName(String configurationName) {
-        return String.format("upload%sInternal", getCapitalName(configurationName));
-    }
-
     public static String uploadTaskName(String configurationName) {
         return String.format("upload%s", getCapitalName(configurationName));
     }
 
-
     private static String getCapitalName(String configurationName) {
         return configurationName.substring(0, 1).toUpperCase() + configurationName.substring(1);
     }
diff --git a/subprojects/core-impl/src/main/groovy/org/gradle/api/internal/artifacts/configurations/ConfigurationsProvider.java b/subprojects/core-impl/src/main/groovy/org/gradle/api/internal/artifacts/configurations/ConfigurationsProvider.java
index 15b95cc..85bce22 100644
--- a/subprojects/core-impl/src/main/groovy/org/gradle/api/internal/artifacts/configurations/ConfigurationsProvider.java
+++ b/subprojects/core-impl/src/main/groovy/org/gradle/api/internal/artifacts/configurations/ConfigurationsProvider.java
@@ -19,9 +19,6 @@ import org.gradle.api.artifacts.Configuration;
 
 import java.util.Set;
 
-/**
- * @author Hans Dockter
- */
 public interface ConfigurationsProvider {
     Set<Configuration> getAll();
 }
diff --git a/subprojects/core-impl/src/main/groovy/org/gradle/api/internal/artifacts/configurations/DefaultConfiguration.java b/subprojects/core-impl/src/main/groovy/org/gradle/api/internal/artifacts/configurations/DefaultConfiguration.java
index 3042e4e..cad63b4 100644
--- a/subprojects/core-impl/src/main/groovy/org/gradle/api/internal/artifacts/configurations/DefaultConfiguration.java
+++ b/subprojects/core-impl/src/main/groovy/org/gradle/api/internal/artifacts/configurations/DefaultConfiguration.java
@@ -411,6 +411,10 @@ public class DefaultConfiguration extends AbstractFileCollection implements Conf
         return resolutionStrategy;
     }
 
+    public String getPath() {
+        return path;
+    }
+
     public Configuration resolutionStrategy(Closure closure) {
         ConfigureUtil.configure(closure, resolutionStrategy);
         return this;
diff --git a/subprojects/core-impl/src/main/groovy/org/gradle/api/internal/artifacts/configurations/DefaultConfigurationContainer.java b/subprojects/core-impl/src/main/groovy/org/gradle/api/internal/artifacts/configurations/DefaultConfigurationContainer.java
index 0a2eb49..363e8bf 100644
--- a/subprojects/core-impl/src/main/groovy/org/gradle/api/internal/artifacts/configurations/DefaultConfigurationContainer.java
+++ b/subprojects/core-impl/src/main/groovy/org/gradle/api/internal/artifacts/configurations/DefaultConfigurationContainer.java
@@ -27,14 +27,12 @@ import org.gradle.api.internal.artifacts.ConfigurationResolver;
 import org.gradle.api.internal.artifacts.ivyservice.resolutionstrategy.DefaultResolutionStrategy;
 import org.gradle.internal.reflect.Instantiator;
 import org.gradle.listener.ListenerManager;
+import org.gradle.util.DeprecationLogger;
 
 import java.util.Collection;
 import java.util.Set;
 
-/**
- * @author Hans Dockter
- */
-public class DefaultConfigurationContainer extends AbstractNamedDomainObjectContainer<Configuration> 
+public class DefaultConfigurationContainer extends AbstractNamedDomainObjectContainer<Configuration>
         implements ConfigurationContainerInternal, ConfigurationsProvider {
     public static final String DETACHED_CONFIGURATION_DEFAULT_NAME = "detachedConfiguration";
     
@@ -69,10 +67,12 @@ public class DefaultConfigurationContainer extends AbstractNamedDomainObjectCont
     }
 
     public Configuration add(String name) {
+        DeprecationLogger.nagUserOfReplacedMethod("ConfigurationContainer.add()", "create()");
         return create(name);
     }
 
     public Configuration add(String name, Closure closure) {
+        DeprecationLogger.nagUserOfReplacedMethod("ConfigurationContainer.add()", "create()");
         return create(name, closure);
     }
 
diff --git a/subprojects/core-impl/src/main/groovy/org/gradle/api/internal/artifacts/configurations/DetachedConfigurationsProvider.java b/subprojects/core-impl/src/main/groovy/org/gradle/api/internal/artifacts/configurations/DetachedConfigurationsProvider.java
index c8c83ff..7a61bae 100644
--- a/subprojects/core-impl/src/main/groovy/org/gradle/api/internal/artifacts/configurations/DetachedConfigurationsProvider.java
+++ b/subprojects/core-impl/src/main/groovy/org/gradle/api/internal/artifacts/configurations/DetachedConfigurationsProvider.java
@@ -20,9 +20,6 @@ import org.gradle.util.WrapUtil;
 
 import java.util.Set;
 
-/**
- * @author Hans Dockter
-*/
 class DetachedConfigurationsProvider implements ConfigurationsProvider {
     private Configuration theOnlyConfiguration;
 
diff --git a/subprojects/core-impl/src/main/groovy/org/gradle/api/internal/artifacts/dsl/DefaultArtifactHandler.groovy b/subprojects/core-impl/src/main/groovy/org/gradle/api/internal/artifacts/dsl/DefaultArtifactHandler.groovy
index 6fd0bb3..6a68287 100644
--- a/subprojects/core-impl/src/main/groovy/org/gradle/api/internal/artifacts/dsl/DefaultArtifactHandler.groovy
+++ b/subprojects/core-impl/src/main/groovy/org/gradle/api/internal/artifacts/dsl/DefaultArtifactHandler.groovy
@@ -20,13 +20,10 @@ import org.gradle.api.artifacts.Configuration
 import org.gradle.api.artifacts.ConfigurationContainer
 import org.gradle.api.artifacts.PublishArtifact
 import org.gradle.api.artifacts.dsl.ArtifactHandler
+import org.gradle.api.internal.notations.api.NotationParser
 import org.gradle.util.ConfigureUtil
 import org.gradle.util.GUtil
-import org.gradle.api.internal.notations.api.NotationParser
 
-/**
- * @author Hans Dockter
- */
 class DefaultArtifactHandler implements ArtifactHandler {
 
     ConfigurationContainer configurationContainer
diff --git a/subprojects/core-impl/src/main/groovy/org/gradle/api/internal/artifacts/dsl/DefaultComponentMetadataHandler.java b/subprojects/core-impl/src/main/groovy/org/gradle/api/internal/artifacts/dsl/DefaultComponentMetadataHandler.java
new file mode 100644
index 0000000..18fc28b
--- /dev/null
+++ b/subprojects/core-impl/src/main/groovy/org/gradle/api/internal/artifacts/dsl/DefaultComponentMetadataHandler.java
@@ -0,0 +1,43 @@
+/*
+ * Copyright 2013 the original author or authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.gradle.api.internal.artifacts.dsl;
+
+import org.gradle.api.Action;
+import org.gradle.api.artifacts.ComponentMetadataDetails;
+import org.gradle.api.internal.artifacts.ModuleMetadataProcessor;
+import org.gradle.api.artifacts.dsl.ComponentMetadataHandler;
+import org.gradle.api.internal.artifacts.ivyservice.ivyresolve.ModuleVersionMetaData;
+import org.gradle.api.internal.artifacts.repositories.resolver.ComponentMetadataDetailsAdapter;
+import org.gradle.internal.reflect.Instantiator;
+import org.gradle.listener.ActionBroadcast;
+
+public class DefaultComponentMetadataHandler implements ComponentMetadataHandler, ModuleMetadataProcessor {
+    private final Instantiator instantiator;
+    private final ActionBroadcast<ComponentMetadataDetails> moduleRules = new ActionBroadcast<ComponentMetadataDetails>();
+
+    public DefaultComponentMetadataHandler(Instantiator instantiator) {
+        this.instantiator = instantiator;
+    }
+
+    public void eachComponent(Action<? super ComponentMetadataDetails> rule) {
+        moduleRules.add(rule);
+    }
+
+    public void process(ModuleVersionMetaData metadata) {
+        ComponentMetadataDetails details = instantiator.newInstance(ComponentMetadataDetailsAdapter.class, metadata);
+        moduleRules.execute(details);
+    }
+}
diff --git a/subprojects/core-impl/src/main/groovy/org/gradle/api/internal/artifacts/dsl/ModuleVersionSelectorParsers.java b/subprojects/core-impl/src/main/groovy/org/gradle/api/internal/artifacts/dsl/ModuleVersionSelectorParsers.java
index d852d60..2fa896a 100644
--- a/subprojects/core-impl/src/main/groovy/org/gradle/api/internal/artifacts/dsl/ModuleVersionSelectorParsers.java
+++ b/subprojects/core-impl/src/main/groovy/org/gradle/api/internal/artifacts/dsl/ModuleVersionSelectorParsers.java
@@ -30,9 +30,6 @@ import java.util.Set;
 
 import static org.gradle.api.internal.artifacts.DefaultModuleVersionSelector.newSelector;
 
-/**
- * by Szczepan Faber, created at: 10/11/11
- */
 public class ModuleVersionSelectorParsers {
 
     public static NotationParser<Set<ModuleVersionSelector>> multiParser() {
diff --git a/subprojects/core-impl/src/main/groovy/org/gradle/api/internal/artifacts/dsl/ParsedModuleStringNotation.java b/subprojects/core-impl/src/main/groovy/org/gradle/api/internal/artifacts/dsl/ParsedModuleStringNotation.java
index cb31a95..de16d59 100644
--- a/subprojects/core-impl/src/main/groovy/org/gradle/api/internal/artifacts/dsl/ParsedModuleStringNotation.java
+++ b/subprojects/core-impl/src/main/groovy/org/gradle/api/internal/artifacts/dsl/ParsedModuleStringNotation.java
@@ -18,9 +18,6 @@ package org.gradle.api.internal.artifacts.dsl;
 import org.gradle.api.IllegalDependencyNotation;
 import org.gradle.util.GUtil;
 
-/**
- * @author Hans Dockter
- */
 public class ParsedModuleStringNotation {
     private String group;
     private String name;
diff --git a/subprojects/core-impl/src/main/groovy/org/gradle/api/internal/artifacts/dsl/PublishArtifactNotationParserFactory.java b/subprojects/core-impl/src/main/groovy/org/gradle/api/internal/artifacts/dsl/PublishArtifactNotationParserFactory.java
index 0064e2d..c80157e 100644
--- a/subprojects/core-impl/src/main/groovy/org/gradle/api/internal/artifacts/dsl/PublishArtifactNotationParserFactory.java
+++ b/subprojects/core-impl/src/main/groovy/org/gradle/api/internal/artifacts/dsl/PublishArtifactNotationParserFactory.java
@@ -34,9 +34,6 @@ import org.gradle.internal.reflect.Instantiator;
 import java.io.File;
 import java.util.Collection;
 
-/**
- * @author Hans Dockter
- */
 public class PublishArtifactNotationParserFactory implements Factory<NotationParser<PublishArtifact>> {
     private final Instantiator instantiator;
     private final DependencyMetaDataProvider metaDataProvider;
diff --git a/subprojects/core-impl/src/main/groovy/org/gradle/api/internal/artifacts/ivyservice/BuildableArtifactResolveResult.java b/subprojects/core-impl/src/main/groovy/org/gradle/api/internal/artifacts/ivyservice/BuildableArtifactResolveResult.java
index dd43274..4ea37c1 100644
--- a/subprojects/core-impl/src/main/groovy/org/gradle/api/internal/artifacts/ivyservice/BuildableArtifactResolveResult.java
+++ b/subprojects/core-impl/src/main/groovy/org/gradle/api/internal/artifacts/ivyservice/BuildableArtifactResolveResult.java
@@ -16,7 +16,7 @@
 
 package org.gradle.api.internal.artifacts.ivyservice;
 
-import org.apache.ivy.core.module.descriptor.Artifact;
+import org.gradle.api.artifacts.ArtifactIdentifier;
 import org.gradle.api.internal.artifacts.ivyservice.ivyresolve.ArtifactResolveException;
 
 import java.io.File;
@@ -33,7 +33,7 @@ public interface BuildableArtifactResolveResult extends ArtifactResolveResult {
     void failed(ArtifactResolveException failure);
 
     /**
-     * Marks the module version as not found.
+     * Marks the artifact as not found.
      */
-    void notFound(Artifact artifact);
+    void notFound(ArtifactIdentifier artifact);
 }
diff --git a/subprojects/core-impl/src/main/groovy/org/gradle/api/internal/artifacts/ivyservice/DefaultBuildableArtifactResolveResult.java b/subprojects/core-impl/src/main/groovy/org/gradle/api/internal/artifacts/ivyservice/DefaultBuildableArtifactResolveResult.java
index 9e1d486..099f2db 100644
--- a/subprojects/core-impl/src/main/groovy/org/gradle/api/internal/artifacts/ivyservice/DefaultBuildableArtifactResolveResult.java
+++ b/subprojects/core-impl/src/main/groovy/org/gradle/api/internal/artifacts/ivyservice/DefaultBuildableArtifactResolveResult.java
@@ -16,7 +16,7 @@
 
 package org.gradle.api.internal.artifacts.ivyservice;
 
-import org.apache.ivy.core.module.descriptor.Artifact;
+import org.gradle.api.artifacts.ArtifactIdentifier;
 import org.gradle.api.internal.artifacts.ivyservice.ivyresolve.ArtifactNotFoundException;
 import org.gradle.api.internal.artifacts.ivyservice.ivyresolve.ArtifactResolveException;
 
@@ -34,7 +34,7 @@ public class DefaultBuildableArtifactResolveResult implements BuildableArtifactR
         this.file = file;
     }
 
-    public void notFound(Artifact artifact) {
+    public void notFound(ArtifactIdentifier artifact) {
         failed(new ArtifactNotFoundException(artifact));
     }
 
diff --git a/subprojects/core-impl/src/main/groovy/org/gradle/api/internal/artifacts/ivyservice/DefaultBuildableModuleVersionResolveResult.java b/subprojects/core-impl/src/main/groovy/org/gradle/api/internal/artifacts/ivyservice/DefaultBuildableModuleVersionResolveResult.java
index 4410d69..7606036 100644
--- a/subprojects/core-impl/src/main/groovy/org/gradle/api/internal/artifacts/ivyservice/DefaultBuildableModuleVersionResolveResult.java
+++ b/subprojects/core-impl/src/main/groovy/org/gradle/api/internal/artifacts/ivyservice/DefaultBuildableModuleVersionResolveResult.java
@@ -19,7 +19,7 @@ package org.gradle.api.internal.artifacts.ivyservice;
 import org.apache.ivy.core.module.descriptor.ModuleDescriptor;
 import org.gradle.api.artifacts.ModuleVersionIdentifier;
 import org.gradle.api.artifacts.ModuleVersionSelector;
-import org.gradle.api.internal.artifacts.ivyservice.ivyresolve.DefaultBuildableModuleVersionMetaData;
+import org.gradle.api.internal.artifacts.ivyservice.ivyresolve.DefaultBuildableModuleVersionMetaDataResolveResult;
 import org.gradle.api.internal.artifacts.ivyservice.ivyresolve.ModuleVersionMetaData;
 
 public class DefaultBuildableModuleVersionResolveResult implements BuildableModuleVersionResolveResult {
@@ -38,7 +38,7 @@ public class DefaultBuildableModuleVersionResolveResult implements BuildableModu
     }
 
     public void resolved(ModuleVersionIdentifier moduleVersionId, ModuleDescriptor descriptor, ArtifactResolver artifactResolver) {
-        DefaultBuildableModuleVersionMetaData metaData = new DefaultBuildableModuleVersionMetaData();
+        DefaultBuildableModuleVersionMetaDataResolveResult metaData = new DefaultBuildableModuleVersionMetaDataResolveResult();
         metaData.resolved(moduleVersionId, descriptor, false, null);
         resolved(metaData, artifactResolver);
     }
@@ -50,7 +50,7 @@ public class DefaultBuildableModuleVersionResolveResult implements BuildableModu
 
     public void setMetaData(ModuleDescriptor descriptor) {
         assertResolved();
-        DefaultBuildableModuleVersionMetaData newMetaData = new DefaultBuildableModuleVersionMetaData();
+        DefaultBuildableModuleVersionMetaDataResolveResult newMetaData = new DefaultBuildableModuleVersionMetaDataResolveResult();
         newMetaData.resolved(metaData.getId(), descriptor, metaData.isChanging(), null);
         this.metaData = newMetaData;
     }
diff --git a/subprojects/core-impl/src/main/groovy/org/gradle/api/internal/artifacts/ivyservice/DefaultCacheLockingManager.java b/subprojects/core-impl/src/main/groovy/org/gradle/api/internal/artifacts/ivyservice/DefaultCacheLockingManager.java
index 8928b3e..3f9b73c 100644
--- a/subprojects/core-impl/src/main/groovy/org/gradle/api/internal/artifacts/ivyservice/DefaultCacheLockingManager.java
+++ b/subprojects/core-impl/src/main/groovy/org/gradle/api/internal/artifacts/ivyservice/DefaultCacheLockingManager.java
@@ -28,7 +28,8 @@ import java.io.File;
 public class DefaultCacheLockingManager implements CacheLockingManager {
 
     // If you update this, also update DefaultGradleDistribution.getArtifactCacheLayoutVersion() (which is the historical record)
-    public static final int CACHE_LAYOUT_VERSION = 23;
+    // You should also update LocallyAvailableResourceFinderFactory
+    public static final int CACHE_LAYOUT_VERSION = 26;
 
     private final PersistentCache cache;
 
diff --git a/subprojects/core-impl/src/main/groovy/org/gradle/api/internal/artifacts/ivyservice/DefaultDependencyResolveDetails.java b/subprojects/core-impl/src/main/groovy/org/gradle/api/internal/artifacts/ivyservice/DefaultDependencyResolveDetails.java
index b8fa9f1..20e7af0 100644
--- a/subprojects/core-impl/src/main/groovy/org/gradle/api/internal/artifacts/ivyservice/DefaultDependencyResolveDetails.java
+++ b/subprojects/core-impl/src/main/groovy/org/gradle/api/internal/artifacts/ivyservice/DefaultDependencyResolveDetails.java
@@ -24,9 +24,6 @@ import org.gradle.api.internal.artifacts.ivyservice.resolveengine.result.Version
 
 import static org.gradle.api.internal.artifacts.DefaultModuleVersionSelector.newSelector;
 
-/**
-* by Szczepan Faber, created at: 11/29/12
-*/
 public class DefaultDependencyResolveDetails implements DependencyResolveDetailsInternal {
     private final ModuleVersionSelector requested;
     private ModuleVersionSelectionReason selectionReason;
diff --git a/subprojects/core-impl/src/main/groovy/org/gradle/api/internal/artifacts/ivyservice/DefaultIvyContextManager.java b/subprojects/core-impl/src/main/groovy/org/gradle/api/internal/artifacts/ivyservice/DefaultIvyContextManager.java
new file mode 100644
index 0000000..4d8df3b
--- /dev/null
+++ b/subprojects/core-impl/src/main/groovy/org/gradle/api/internal/artifacts/ivyservice/DefaultIvyContextManager.java
@@ -0,0 +1,104 @@
+/*
+ * Copyright 2013 the original author or authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY 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.Ivy;
+import org.apache.ivy.core.IvyContext;
+import org.apache.ivy.core.settings.IvySettings;
+import org.apache.ivy.util.Message;
+import org.gradle.api.Action;
+import org.gradle.api.Transformer;
+import org.gradle.api.internal.Transformers;
+
+import java.util.LinkedList;
+import java.util.concurrent.locks.Lock;
+import java.util.concurrent.locks.ReentrantLock;
+
+public class DefaultIvyContextManager implements IvyContextManager {
+    private static final int MAX_CACHED_IVY_INSTANCES = 4;
+    private final Lock lock = new ReentrantLock();
+    private boolean messageAdapterAttached;
+    private final LinkedList<Ivy> cached = new LinkedList<Ivy>();
+    private final ThreadLocal<Integer> depth = new ThreadLocal<Integer>();
+
+    public void withIvy(final Action<? super Ivy> action) {
+        withIvy(Transformers.toTransformer(action));
+    }
+
+    public <T> T withIvy(Transformer<? extends T, ? super Ivy> action) {
+        Integer currentDepth = depth.get();
+
+        if (currentDepth != null) {
+            depth.set(currentDepth + 1);
+            try {
+                return action.transform(IvyContext.getContext().getIvy());
+            } finally {
+                depth.set(currentDepth);
+            }
+        }
+
+        IvyContext.pushNewContext();
+        try {
+            depth.set(1);
+            try {
+                Ivy ivy = getIvy();
+                try {
+                    IvyContext.getContext().setIvy(ivy);
+                    return action.transform(ivy);
+                } finally {
+                    releaseIvy(ivy);
+                }
+            } finally {
+                depth.set(null);
+            }
+        } finally {
+            IvyContext.popContext();
+        }
+    }
+
+    private Ivy getIvy() {
+        lock.lock();
+        try {
+            if (!cached.isEmpty()) {
+                return cached.removeFirst();
+            }
+            if (!messageAdapterAttached) {
+                Message.setDefaultLogger(new IvyLoggingAdaper());
+                messageAdapterAttached = true;
+            }
+        } finally {
+            lock.unlock();
+        }
+        return Ivy.newInstance(new IvySettings());
+    }
+
+    private void releaseIvy(Ivy ivy) {
+        // cleanup
+        ivy.getSettings().getResolvers().clear();
+        ivy.getSettings().setDefaultResolver(null);
+
+        lock.lock();
+        try {
+            if (cached.size() < MAX_CACHED_IVY_INSTANCES) {
+                cached.add(ivy);
+            }
+            // else, throw it away
+        } finally {
+            lock.unlock();
+        }
+    }
+}
diff --git a/subprojects/core-impl/src/main/groovy/org/gradle/api/internal/artifacts/ivyservice/DefaultIvyDependencyPublisher.java b/subprojects/core-impl/src/main/groovy/org/gradle/api/internal/artifacts/ivyservice/DefaultIvyDependencyPublisher.java
index 6b78236..3f3fd50 100644
--- a/subprojects/core-impl/src/main/groovy/org/gradle/api/internal/artifacts/ivyservice/DefaultIvyDependencyPublisher.java
+++ b/subprojects/core-impl/src/main/groovy/org/gradle/api/internal/artifacts/ivyservice/DefaultIvyDependencyPublisher.java
@@ -15,146 +15,82 @@
  */
 package org.gradle.api.internal.artifacts.ivyservice;
 
-import org.apache.ivy.core.IvyContext;
-import org.apache.ivy.core.event.EventManager;
-import org.apache.ivy.core.event.publish.EndArtifactPublishEvent;
-import org.apache.ivy.core.event.publish.StartArtifactPublishEvent;
 import org.apache.ivy.core.module.descriptor.Artifact;
+import org.apache.ivy.core.module.descriptor.DefaultModuleDescriptor;
 import org.apache.ivy.core.module.descriptor.MDArtifact;
-import org.apache.ivy.core.module.descriptor.ModuleDescriptor;
-import org.apache.ivy.plugins.resolver.DependencyResolver;
-import org.apache.ivy.util.ConfigurationUtils;
 import org.gradle.api.UncheckedIOException;
+import org.gradle.api.internal.artifacts.DefaultModuleVersionPublishMetaData;
+import org.gradle.api.internal.artifacts.ModuleVersionPublishMetaData;
+import org.gradle.api.internal.artifacts.ModuleVersionPublisher;
 import org.gradle.util.DeprecationLogger;
 import org.slf4j.Logger;
 import org.slf4j.LoggerFactory;
 
 import java.io.File;
 import java.io.IOException;
-import java.util.*;
+import java.util.List;
+import java.util.Map;
+import java.util.Set;
 
-/**
- * @author Hans Dockter
- */
 public class DefaultIvyDependencyPublisher implements IvyDependencyPublisher {
-    public static final String FILE_ABSOLUTE_PATH_EXTRA_ATTRIBUTE = "filePath";
-
     private static Logger logger = LoggerFactory.getLogger(DefaultIvyDependencyPublisher.class);
 
     public void publish(Set<String> configurations,
-                        List<DependencyResolver> publishResolvers,
-                        ModuleDescriptor moduleDescriptor,
-                        File descriptorDestination,
-                        EventManager eventManager) {
+                        List<ModuleVersionPublisher> publishResolvers,
+                        ModuleVersionPublishMetaData publishMetaData,
+                        File descriptorDestination) {
         try {
-            Publication publication = new Publication(moduleDescriptor, eventManager, configurations, descriptorDestination);
-
-            for (DependencyResolver resolver : publishResolvers) {
-                logger.info("Publishing to repository {}", resolver);
-                publication.publishTo(resolver);
+            Publication publication = new Publication(publishMetaData, descriptorDestination);
+            for (ModuleVersionPublisher publisher : publishResolvers) {
+                logger.info("Publishing to {}", publisher);
+                publisher.publish(publication);
             }
         } catch (IOException e) {
             throw new UncheckedIOException(e);
         }
     }
 
-    private static class Publication {
-        private final ModuleDescriptor moduleDescriptor;
-        private final Set<String> configurations;
+    private static class Publication extends DefaultModuleVersionPublishMetaData {
         private final File descriptorFile;
 
-        private final EventManager eventManager;
-
-        private Publication(ModuleDescriptor moduleDescriptor, EventManager eventManager, Set<String> configurations, File descriptorFile) {
-            this.moduleDescriptor = moduleDescriptor;
-            this.eventManager = eventManager;
-            this.configurations = configurations;
+        private Publication(ModuleVersionPublishMetaData metaData, File descriptorFile) {
+            super((DefaultModuleDescriptor) metaData.getModuleDescriptor());
             this.descriptorFile = descriptorFile;
-        }
-
-        public void publishTo(DependencyResolver resolver) throws IOException {
-            Set<Artifact> allArtifacts = getAllArtifacts(moduleDescriptor);
-
-            Map<Artifact, File> artifactsFiles = new LinkedHashMap<Artifact, File>();
-            for (Artifact artifact : allArtifacts) {
-                addPublishedArtifact(artifact, artifactsFiles);
+            for (Map.Entry<Artifact, File> entry : metaData.getArtifacts().entrySet()) {
+                addPublishedArtifact(entry.getKey(), entry.getValue());
             }
             if (descriptorFile != null) {
-                addPublishedDescriptor(artifactsFiles);
-            }
-
-            boolean successfullyPublished = false;
-            try {
-                resolver.beginPublishTransaction(moduleDescriptor.getModuleRevisionId(), true);
-                // for each declared published artifact in this descriptor, do:
-                for (Map.Entry<Artifact, File> entry : artifactsFiles.entrySet()) {
-                    Artifact artifact = entry.getKey();
-                    File artifactFile = entry.getValue();
-                    publish(artifact, artifactFile, resolver, true);
-                }
-                resolver.commitPublishTransaction();
-                successfullyPublished = true;
-            } finally {
-                if (!successfullyPublished) {
-                    resolver.abortPublishTransaction();
-                }
+                addPublishedDescriptor();
             }
         }
 
-        private void addPublishedDescriptor(Map<Artifact, File> artifactsFiles) {
-            Artifact artifact = MDArtifact.newIvyArtifact(moduleDescriptor);
-            checkArtifactFileExists(artifact, descriptorFile);
-            artifactsFiles.put(artifact, descriptorFile);
+        private void addPublishedDescriptor() {
+            Artifact artifact = MDArtifact.newIvyArtifact(getModuleDescriptor());
+            if (checkArtifactFileExists(artifact, descriptorFile)) {
+                addArtifact(artifact, descriptorFile);
+            }
         }
 
-        private void addPublishedArtifact(Artifact artifact, Map<Artifact, File> artifactsFiles) {
-            File artifactFile = new File(artifact.getExtraAttribute(FILE_ABSOLUTE_PATH_EXTRA_ATTRIBUTE));
-            checkArtifactFileExists(artifact, artifactFile);
-            artifactsFiles.put(artifact, artifactFile);
+        private void addPublishedArtifact(Artifact artifact, File artifactFile) {
+            if (checkArtifactFileExists(artifact, artifactFile)) {
+                addArtifact(artifact, artifactFile);
+            }
         }
 
-        private void checkArtifactFileExists(Artifact artifact, File artifactFile) {
-            if (!artifactFile.exists()) {
-                // TODO:DAZ This hack is required so that we don't log a warning when the Signing plugin is used. We need to allow conditional configurations so we can remove this.
-                if (isSigningArtifact(artifact)) {
-                    return;
-                }
+        private boolean checkArtifactFileExists(Artifact artifact, File artifactFile) {
+            if (artifactFile.exists()) {
+                return true;
+            }
+            // TODO:DAZ This hack is required so that we don't log a warning when the Signing plugin is used. We need to allow conditional configurations so we can remove this.
+            if (!isSigningArtifact(artifact)) {
                 String message = String.format("Attempted to publish an artifact '%s' that does not exist '%s'", artifact.getModuleRevisionId(), artifactFile);
                 DeprecationLogger.nagUserOfDeprecatedBehaviour(message);
             }
+            return false;
         }
 
         private boolean isSigningArtifact(Artifact artifact) {
             return artifact.getType().endsWith(".asc") || artifact.getType().endsWith(".sig");
         }
-
-        private Set<Artifact> getAllArtifacts(ModuleDescriptor moduleDescriptor) {
-            Set<Artifact> allArtifacts = new LinkedHashSet<Artifact>();
-            String[] trueConfigurations = ConfigurationUtils.replaceWildcards(configurations.toArray(new String[configurations.size()]), moduleDescriptor);
-            for (String configuration : trueConfigurations) {
-                Collections.addAll(allArtifacts, moduleDescriptor.getArtifacts(configuration));
-            }
-            return allArtifacts;
-        }
-
-        private void publish(Artifact artifact, File src,
-                             DependencyResolver resolver, boolean overwrite) throws IOException {
-            IvyContext.getContext().checkInterrupted();
-            //notify triggers that an artifact is about to be published
-            eventManager.fireIvyEvent(
-                    new StartArtifactPublishEvent(resolver, artifact, src, overwrite));
-            boolean successful = false; //set to true once the publish succeeds
-            try {
-                if (src.exists()) {
-                    resolver.publish(artifact, src, overwrite);
-                    successful = true;
-                }
-            } finally {
-                //notify triggers that the publish is finished, successfully or not.
-                eventManager.fireIvyEvent(
-                        new EndArtifactPublishEvent(resolver, artifact, src, overwrite, successful));
-            }
-        }
     }
-
 }
diff --git a/subprojects/core-impl/src/main/groovy/org/gradle/api/internal/artifacts/ivyservice/DefaultIvyFactory.java b/subprojects/core-impl/src/main/groovy/org/gradle/api/internal/artifacts/ivyservice/DefaultIvyFactory.java
deleted file mode 100644
index 7d42d31..0000000
--- a/subprojects/core-impl/src/main/groovy/org/gradle/api/internal/artifacts/ivyservice/DefaultIvyFactory.java
+++ /dev/null
@@ -1,38 +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.ivyservice;
-
-import org.apache.ivy.Ivy;
-import org.apache.ivy.core.settings.IvySettings;
-
-import java.util.HashMap;
-import java.util.Map;
-
-/**
- * @author Hans Dockter
- */
-public class DefaultIvyFactory implements IvyFactory {
-    private final Map<IvySettings, Ivy> instanceCache = new HashMap<IvySettings, Ivy>();
-    
-    public Ivy createIvy(IvySettings ivySettings) {
-        Ivy ivy = instanceCache.get(ivySettings);
-        if (ivy == null) {
-            ivy = Ivy.newInstance(ivySettings);
-            instanceCache.put(ivySettings, ivy);
-        }
-        return ivy;
-    }
-}
diff --git a/subprojects/core-impl/src/main/groovy/org/gradle/api/internal/artifacts/ivyservice/DefaultLenientConfiguration.java b/subprojects/core-impl/src/main/groovy/org/gradle/api/internal/artifacts/ivyservice/DefaultLenientConfiguration.java
index 1cf335d..e296b08 100644
--- a/subprojects/core-impl/src/main/groovy/org/gradle/api/internal/artifacts/ivyservice/DefaultLenientConfiguration.java
+++ b/subprojects/core-impl/src/main/groovy/org/gradle/api/internal/artifacts/ivyservice/DefaultLenientConfiguration.java
@@ -16,37 +16,37 @@
 package org.gradle.api.internal.artifacts.ivyservice;
 
 import org.gradle.api.artifacts.*;
-import org.gradle.api.internal.CachingDirectedGraphWalker;
-import org.gradle.api.internal.DirectedGraphWithEdgeValues;
 import org.gradle.api.internal.artifacts.ivyservice.ivyresolve.ArtifactResolveException;
+import org.gradle.api.internal.artifacts.ivyservice.resolveengine.oldresult.ResolvedConfigurationResults;
 import org.gradle.api.specs.Spec;
+import org.gradle.api.specs.Specs;
+import org.gradle.internal.Factory;
+import org.gradle.internal.graph.CachingDirectedGraphWalker;
+import org.gradle.internal.graph.DirectedGraphWithEdgeValues;
 import org.gradle.util.CollectionUtils;
 
 import java.io.File;
 import java.util.*;
 
-public class DefaultLenientConfiguration implements ResolvedConfigurationBuilder, LenientConfiguration {
-    private final ResolvedDependency root;
+public class DefaultLenientConfiguration implements LenientConfiguration {
+    private CacheLockingManager cacheLockingManager;
     private final Configuration configuration;
-    private final Map<ModuleDependency, ResolvedDependency> firstLevelDependencies = new LinkedHashMap<ModuleDependency, ResolvedDependency>();
-    private final Set<ResolvedArtifact> artifacts = new LinkedHashSet<ResolvedArtifact>();
-    private final Set<UnresolvedDependency> unresolvedDependencies = new LinkedHashSet<UnresolvedDependency>();
-    private final CachingDirectedGraphWalker<ResolvedDependency, ResolvedArtifact> walker
-            = new CachingDirectedGraphWalker<ResolvedDependency, ResolvedArtifact>(new ResolvedDependencyArtifactsGraph());
+    private ResolvedConfigurationResults results;
 
-    public DefaultLenientConfiguration(Configuration configuration, ResolvedDependency root) {
+    public DefaultLenientConfiguration(Configuration configuration, ResolvedConfigurationResults results, CacheLockingManager cacheLockingManager) {
         this.configuration = configuration;
-        this.root = root;
+        this.results = results;
+        this.cacheLockingManager = cacheLockingManager;
     }
 
     public boolean hasError() {
-        return !unresolvedDependencies.isEmpty();
+        return results.hasError();
     }
 
     public void rethrowFailure() throws ResolveException {
-        if (!unresolvedDependencies.isEmpty()) {
+        if (hasError()) {
             List<Throwable> failures = new ArrayList<Throwable>();
-            for (UnresolvedDependency unresolvedDependency : unresolvedDependencies) {
+            for (UnresolvedDependency unresolvedDependency : results.getUnresolvedDependencies()) {
                 failures.add(unresolvedDependency.getProblem());
             }
             throw new ResolveException(configuration, failures);
@@ -54,36 +54,16 @@ public class DefaultLenientConfiguration implements ResolvedConfigurationBuilder
     }
 
     public Set<UnresolvedDependency> getUnresolvedModuleDependencies() {
-        return unresolvedDependencies;
+        return results.getUnresolvedDependencies();
     }
 
     public Set<ResolvedArtifact> getResolvedArtifacts() throws ResolveException {
-        return artifacts;
-    }
-
-    public ResolvedDependency getRoot() {
-        return root;
-    }
-
-    public void addFirstLevelDependency(ModuleDependency moduleDependency, ResolvedDependency refersTo) {
-        firstLevelDependencies.put(moduleDependency, refersTo);
-    }
-
-    public void addArtifact(ResolvedArtifact artifact) {
-        artifacts.add(artifact);
-    }
-
-    public void addUnresolvedDependency(UnresolvedDependency unresolvedDependency) {
-        unresolvedDependencies.add(unresolvedDependency);
-    }
-
-    public Set<ResolvedDependency> getFirstLevelModuleDependencies() {
-        return root.getChildren();
+        return results.getArtifacts();
     }
 
     public Set<ResolvedDependency> getFirstLevelModuleDependencies(Spec<? super Dependency> dependencySpec) {
         Set<ResolvedDependency> matches = new LinkedHashSet<ResolvedDependency>();
-        for (Map.Entry<ModuleDependency, ResolvedDependency> entry : firstLevelDependencies.entrySet()) {
+        for (Map.Entry<ModuleDependency, ResolvedDependency> entry : results.more().getFirstLevelDependencies().entrySet()) {
             if (dependencySpec.isSatisfiedBy(entry.getKey())) {
                 matches.add(entry.getValue());
             }
@@ -107,27 +87,35 @@ public class DefaultLenientConfiguration implements ResolvedConfigurationBuilder
      * @param dependencySpec dependency spec
      */
     public Set<ResolvedArtifact> getArtifacts(Spec<? super Dependency> dependencySpec) {
-        Set<ResolvedArtifact> allArtifacts = getAllArtifacts(dependencySpec);
-        return CollectionUtils.filter(allArtifacts, new Spec<ResolvedArtifact>() {
-            public boolean isSatisfiedBy(ResolvedArtifact element) {
-                try {
-                    File file = element.getFile();
-                    return file != null;
-                } catch (ArtifactResolveException e) {
-                    return false;
-                }
+        final Set<ResolvedArtifact> allArtifacts = getAllArtifacts(dependencySpec);
+        return cacheLockingManager.useCache("retrieve artifacts from " + configuration, new Factory<Set<ResolvedArtifact>>() {
+            public Set<ResolvedArtifact> create() {
+                return CollectionUtils.filter(allArtifacts, new Spec<ResolvedArtifact>() {
+                    public boolean isSatisfiedBy(ResolvedArtifact element) {
+                        try {
+                            File file = element.getFile();
+                            return file != null;
+                        } catch (ArtifactResolveException e) {
+                            return false;
+                        }
+                    }
+                });
             }
         });
     }
 
-    private Set<File> getFiles(Set<ResolvedArtifact> artifacts) {
-        Set<File> files = new LinkedHashSet<File>();
-        for (ResolvedArtifact artifact : artifacts) {
-            File depFile = artifact.getFile();
-            if (depFile != null) {
-                files.add(depFile);
+    private Set<File> getFiles(final Set<ResolvedArtifact> artifacts) {
+        final Set<File> files = new LinkedHashSet<File>();
+        cacheLockingManager.useCache("resolve files from " + configuration, new Runnable() {
+            public void run() {
+                for (ResolvedArtifact artifact : artifacts) {
+                    File depFile = artifact.getFile();
+                    if (depFile != null) {
+                        files.add(depFile);
+                    }
+                }
             }
-        }
+        });
         return files;
     }
 
@@ -137,12 +125,21 @@ public class DefaultLenientConfiguration implements ResolvedConfigurationBuilder
      * @param dependencySpec dependency spec
      */
     public Set<ResolvedArtifact> getAllArtifacts(Spec<? super Dependency> dependencySpec) {
+        //this is not very nice might be good enough until we get rid of ResolvedConfiguration and friends
+        //avoid traversing the graph causing the full ResolvedDependency graph to be loaded for the most typical scenario
+        if (dependencySpec == Specs.SATISFIES_ALL) {
+            return results.getArtifacts();
+        }
+
+        CachingDirectedGraphWalker<ResolvedDependency, ResolvedArtifact> walker
+                = new CachingDirectedGraphWalker<ResolvedDependency, ResolvedArtifact>(new ResolvedDependencyArtifactsGraph());
+
         Set<ResolvedDependency> firstLevelModuleDependencies = getFirstLevelModuleDependencies(dependencySpec);
 
         Set<ResolvedArtifact> artifacts = new LinkedHashSet<ResolvedArtifact>();
 
         for (ResolvedDependency resolvedDependency : firstLevelModuleDependencies) {
-            artifacts.addAll(resolvedDependency.getParentArtifacts(root));
+            artifacts.addAll(resolvedDependency.getParentArtifacts(results.more().getRoot()));
             walker.add(resolvedDependency);
         }
 
@@ -154,9 +151,13 @@ public class DefaultLenientConfiguration implements ResolvedConfigurationBuilder
         return configuration;
     }
 
+    public Set<ResolvedDependency> getFirstLevelModuleDependencies() {
+        return results.more().getRoot().getChildren();
+    }
+
     private static class ResolvedDependencyArtifactsGraph implements DirectedGraphWithEdgeValues<ResolvedDependency, ResolvedArtifact> {
-        public void getNodeValues(ResolvedDependency node, Collection<ResolvedArtifact> values,
-                                  Collection<ResolvedDependency> connectedNodes) {
+        public void getNodeValues(ResolvedDependency node, Collection<? super ResolvedArtifact> values,
+                                  Collection<? super ResolvedDependency> connectedNodes) {
             connectedNodes.addAll(node.getChildren());
         }
 
diff --git a/subprojects/core-impl/src/main/groovy/org/gradle/api/internal/artifacts/ivyservice/DefaultResolvedConfiguration.java b/subprojects/core-impl/src/main/groovy/org/gradle/api/internal/artifacts/ivyservice/DefaultResolvedConfiguration.java
index a0869af..146ccbe 100644
--- a/subprojects/core-impl/src/main/groovy/org/gradle/api/internal/artifacts/ivyservice/DefaultResolvedConfiguration.java
+++ b/subprojects/core-impl/src/main/groovy/org/gradle/api/internal/artifacts/ivyservice/DefaultResolvedConfiguration.java
@@ -17,20 +17,15 @@ package org.gradle.api.internal.artifacts.ivyservice;
 
 import org.gradle.api.artifacts.*;
 import org.gradle.api.specs.Spec;
-import org.gradle.internal.Factory;
 
 import java.io.File;
 import java.util.Set;
 
-import static java.lang.String.format;
-
 public class DefaultResolvedConfiguration implements ResolvedConfiguration {
     private final DefaultLenientConfiguration configuration;
-    private final CacheLockingManager cacheLockingManager;
 
-    public DefaultResolvedConfiguration(DefaultLenientConfiguration configuration, CacheLockingManager cacheLockingManager) {
+    public DefaultResolvedConfiguration(DefaultLenientConfiguration configuration) {
         this.configuration = configuration;
-        this.cacheLockingManager = cacheLockingManager;
     }
 
     public boolean hasError() {
@@ -47,11 +42,7 @@ public class DefaultResolvedConfiguration implements ResolvedConfiguration {
 
     public Set<File> getFiles(final Spec<? super Dependency> dependencySpec) throws ResolveException {
         rethrowFailure();
-        return cacheLockingManager.useCache(format("resolving files from %s", configuration.getConfiguration()), new Factory<Set<File>>() {
-            public Set<File> create() {
-                return configuration.getFilesStrict(dependencySpec);
-            }
-        });
+        return configuration.getFilesStrict(dependencySpec);
     }
 
     public Set<ResolvedDependency> getFirstLevelModuleDependencies() throws ResolveException {
diff --git a/subprojects/core-impl/src/main/groovy/org/gradle/api/internal/artifacts/ivyservice/DefaultSettingsConverter.java b/subprojects/core-impl/src/main/groovy/org/gradle/api/internal/artifacts/ivyservice/DefaultSettingsConverter.java
deleted file mode 100644
index 4c37d0b..0000000
--- a/subprojects/core-impl/src/main/groovy/org/gradle/api/internal/artifacts/ivyservice/DefaultSettingsConverter.java
+++ /dev/null
@@ -1,70 +0,0 @@
-/*
- * Copyright 2010 the original author or authors.
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- *      http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package org.gradle.api.internal.artifacts.ivyservice;
-
-import org.apache.ivy.core.settings.IvySettings;
-import org.apache.ivy.plugins.resolver.DependencyResolver;
-import org.apache.ivy.util.Message;
-import org.gradle.internal.Factory;
-
-import java.util.List;
-
-/**
- * @author Hans Dockter
- */
-public class DefaultSettingsConverter implements SettingsConverter {
-    private final Factory<IvySettings> settingsFactory;
-    private IvySettings publishSettings;
-    private IvySettings resolveSettings;
-
-    public DefaultSettingsConverter(Factory<IvySettings> settingsFactory) {
-        this.settingsFactory = settingsFactory;
-        Message.setDefaultLogger(new IvyLoggingAdaper());
-    }
-
-    public IvySettings convertForPublish(List<DependencyResolver> publishResolvers) {
-        if (publishSettings == null) {
-            publishSettings = settingsFactory.create();
-        } else {
-            publishSettings.getResolvers().clear();
-        }
-        for (DependencyResolver dependencyResolver : publishResolvers) {
-            dependencyResolver.setSettings(publishSettings);
-        }
-        return publishSettings;
-    }
-
-    public IvySettings convertForResolve(DependencyResolver defaultResolver) {
-        if (resolveSettings == null) {
-            resolveSettings = settingsFactory.create();
-        } else {
-            resolveSettings.getResolvers().clear();
-        }
-
-        resolveSettings.addResolver(defaultResolver);
-        resolveSettings.setDefaultResolver(defaultResolver.getName());
-        
-        return resolveSettings;
-    }
-
-    public IvySettings getForResolve() {
-        if (resolveSettings == null) {
-            resolveSettings = settingsFactory.create();
-        }
-        return resolveSettings;
-    }
-}
diff --git a/subprojects/core-impl/src/main/groovy/org/gradle/api/internal/artifacts/ivyservice/DependencyToModuleResolver.java b/subprojects/core-impl/src/main/groovy/org/gradle/api/internal/artifacts/ivyservice/DependencyToModuleResolver.java
deleted file mode 100755
index 73b9c3d..0000000
--- a/subprojects/core-impl/src/main/groovy/org/gradle/api/internal/artifacts/ivyservice/DependencyToModuleResolver.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.api.internal.artifacts.ivyservice;
-
-import org.gradle.api.internal.artifacts.ivyservice.ivyresolve.DependencyMetaData;
-
-/**
- * Resolves a dependency to the meta-data for a module.
- */
-public interface DependencyToModuleResolver {
-    /**
-     * Resolves the given dependency to a module version id. Failures are packaged up in the returned result.
-     */
-    void resolve(DependencyMetaData dependency, BuildableModuleVersionResolveResult result);
-}
diff --git a/subprojects/core-impl/src/main/groovy/org/gradle/api/internal/artifacts/ivyservice/DependencyToModuleVersionResolver.java b/subprojects/core-impl/src/main/groovy/org/gradle/api/internal/artifacts/ivyservice/DependencyToModuleVersionResolver.java
new file mode 100755
index 0000000..d4fc489
--- /dev/null
+++ b/subprojects/core-impl/src/main/groovy/org/gradle/api/internal/artifacts/ivyservice/DependencyToModuleVersionResolver.java
@@ -0,0 +1,28 @@
+/*
+ * Copyright 2013 the original author or authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY 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.gradle.api.internal.artifacts.ivyservice.ivyresolve.DependencyMetaData;
+
+/**
+ * Resolves a dependency to the meta-data for a module.
+ */
+public interface DependencyToModuleVersionResolver {
+    /**
+     * Resolves the given dependency to a module version id. Failures are packaged up in the returned result.
+     */
+    void resolve(DependencyMetaData dependency, BuildableModuleVersionResolveResult result);
+}
diff --git a/subprojects/core-impl/src/main/groovy/org/gradle/api/internal/artifacts/ivyservice/ErrorHandlingArtifactDependencyResolver.java b/subprojects/core-impl/src/main/groovy/org/gradle/api/internal/artifacts/ivyservice/ErrorHandlingArtifactDependencyResolver.java
index 444809a..915315f 100644
--- a/subprojects/core-impl/src/main/groovy/org/gradle/api/internal/artifacts/ivyservice/ErrorHandlingArtifactDependencyResolver.java
+++ b/subprojects/core-impl/src/main/groovy/org/gradle/api/internal/artifacts/ivyservice/ErrorHandlingArtifactDependencyResolver.java
@@ -15,7 +15,12 @@
  */
 package org.gradle.api.internal.artifacts.ivyservice;
 
+import groovy.lang.Closure;
+import org.gradle.api.Action;
 import org.gradle.api.artifacts.*;
+import org.gradle.api.artifacts.result.DependencyResult;
+import org.gradle.api.artifacts.result.ResolutionResult;
+import org.gradle.api.artifacts.result.ResolvedModuleVersionResult;
 import org.gradle.api.internal.artifacts.ArtifactDependencyResolver;
 import org.gradle.api.internal.artifacts.ResolverResults;
 import org.gradle.api.internal.artifacts.configurations.ConfigurationInternal;
@@ -40,8 +45,9 @@ public class ErrorHandlingArtifactDependencyResolver implements ArtifactDependen
         } catch (final Throwable e) {
             return new ResolverResults(new BrokenResolvedConfiguration(e, configuration), wrapException(e, configuration));
         }
-        ResolvedConfiguration withErrorHandling = new ErrorHandlingResolvedConfiguration(results.getResolvedConfiguration(), configuration);
-        return results.withResolvedConfiguration(withErrorHandling);
+        ResolvedConfiguration wrappedConfiguration = new ErrorHandlingResolvedConfiguration(results.getResolvedConfiguration(), configuration);
+        ResolutionResult wrappedResult = new ErrorHandlingResolutionResult(results.getResolutionResult(), configuration);
+        return new ResolverResults(wrappedConfiguration, wrappedResult);
     }
 
     private static ResolveException wrapException(Throwable e, Configuration configuration) {
@@ -51,6 +57,98 @@ public class ErrorHandlingArtifactDependencyResolver implements ArtifactDependen
         return new ResolveException(configuration, e);
     }
 
+    private static class ErrorHandlingLenientConfiguration implements LenientConfiguration {
+        private final LenientConfiguration lenientConfiguration;
+        private final Configuration configuration;
+
+        private ErrorHandlingLenientConfiguration(LenientConfiguration lenientConfiguration, Configuration configuration) {
+            this.lenientConfiguration = lenientConfiguration;
+            this.configuration = configuration;
+        }
+
+        public Set<ResolvedArtifact> getArtifacts(Spec<? super Dependency> dependencySpec) {
+            try {
+                return lenientConfiguration.getArtifacts(dependencySpec);
+            } catch (Throwable e) {
+                throw wrapException(e, configuration);
+            }
+        }
+
+        public Set<ResolvedDependency> getFirstLevelModuleDependencies(Spec<? super Dependency> dependencySpec) {
+            try {
+                return lenientConfiguration.getFirstLevelModuleDependencies(dependencySpec);
+            } catch (Throwable e) {
+                throw wrapException(e, configuration);
+            }
+        }
+
+        public Set<UnresolvedDependency> getUnresolvedModuleDependencies() {
+            try {
+                return lenientConfiguration.getUnresolvedModuleDependencies();
+            } catch (Throwable e) {
+                throw wrapException(e, configuration);
+            }
+        }
+
+        public Set<File> getFiles(Spec<? super Dependency> dependencySpec) {
+            try {
+                return lenientConfiguration.getFiles(dependencySpec);
+            } catch (Throwable e) {
+                throw wrapException(e, configuration);
+            }
+        }
+    }
+
+    private static class ErrorHandlingResolutionResult implements ResolutionResult {
+        private final ResolutionResult resolutionResult;
+        private final Configuration configuration;
+
+        public ErrorHandlingResolutionResult(ResolutionResult resolutionResult, Configuration configuration) {
+            this.resolutionResult = resolutionResult;
+            this.configuration = configuration;
+        }
+
+        public ResolvedModuleVersionResult getRoot() {
+            try {
+                return resolutionResult.getRoot();
+            } catch (Throwable e) {
+                throw wrapException(e, configuration);
+            }
+        }
+
+        public void allDependencies(Action<? super DependencyResult> action) {
+            resolutionResult.allDependencies(action);
+        }
+
+        public Set<? extends DependencyResult> getAllDependencies() {
+            try {
+                return resolutionResult.getAllDependencies();
+            } catch (Throwable e) {
+                throw wrapException(e, configuration);
+            }
+        }
+
+        public void allDependencies(Closure closure) {
+            resolutionResult.allDependencies(closure);
+        }
+
+        public Set<ResolvedModuleVersionResult> getAllModuleVersions() {
+            try {
+                return resolutionResult.getAllModuleVersions();
+            } catch (Throwable e) {
+                throw wrapException(e, configuration);
+            }
+        }
+
+        public void allModuleVersions(Action<? super ResolvedModuleVersionResult> action) {
+            resolutionResult.allModuleVersions(action);
+        }
+
+        public void allModuleVersions(Closure closure) {
+            resolutionResult.allModuleVersions(closure);
+        }
+    }
+    
     private static class ErrorHandlingResolvedConfiguration implements ResolvedConfiguration {
         private final ResolvedConfiguration resolvedConfiguration;
         private final Configuration configuration;
@@ -66,7 +164,11 @@ public class ErrorHandlingArtifactDependencyResolver implements ArtifactDependen
         }
 
         public LenientConfiguration getLenientConfiguration() {
-            return resolvedConfiguration.getLenientConfiguration();
+            try {
+                return new ErrorHandlingLenientConfiguration(resolvedConfiguration.getLenientConfiguration(), configuration);
+            } catch (Throwable e) {
+                throw wrapException(e, configuration);
+            }
         }
 
         public void rethrowFailure() throws ResolveException {
diff --git a/subprojects/core-impl/src/main/groovy/org/gradle/api/internal/artifacts/ivyservice/IvyBackedArtifactPublisher.java b/subprojects/core-impl/src/main/groovy/org/gradle/api/internal/artifacts/ivyservice/IvyBackedArtifactPublisher.java
index f34fe63..82daa44 100644
--- a/subprojects/core-impl/src/main/groovy/org/gradle/api/internal/artifacts/ivyservice/IvyBackedArtifactPublisher.java
+++ b/subprojects/core-impl/src/main/groovy/org/gradle/api/internal/artifacts/ivyservice/IvyBackedArtifactPublisher.java
@@ -16,11 +16,14 @@
 package org.gradle.api.internal.artifacts.ivyservice;
 
 import org.apache.ivy.Ivy;
-import org.apache.ivy.plugins.resolver.DependencyResolver;
+import org.apache.ivy.core.module.descriptor.ModuleDescriptor;
+import org.gradle.api.Action;
 import org.gradle.api.artifacts.Configuration;
 import org.gradle.api.artifacts.Module;
 import org.gradle.api.artifacts.PublishException;
 import org.gradle.api.internal.artifacts.ArtifactPublisher;
+import org.gradle.api.internal.artifacts.ModuleVersionPublishMetaData;
+import org.gradle.api.internal.artifacts.ModuleVersionPublisher;
 import org.gradle.api.internal.artifacts.configurations.Configurations;
 import org.gradle.api.internal.artifacts.repositories.PublicationAwareRepository;
 
@@ -29,42 +32,47 @@ import java.util.ArrayList;
 import java.util.List;
 import java.util.Set;
 
-/**
- * @author Hans Dockter
- */
 public class IvyBackedArtifactPublisher implements ArtifactPublisher {
-    private final SettingsConverter settingsConverter;
     private final ModuleDescriptorConverter publishModuleDescriptorConverter;
-    private final IvyFactory ivyFactory;
+    private final IvyContextManager ivyContextManager;
     private final IvyDependencyPublisher dependencyPublisher;
+    private final IvyModuleDescriptorWriter ivyModuleDescriptorWriter;
 
-    public IvyBackedArtifactPublisher(SettingsConverter settingsConverter,
-                                      ModuleDescriptorConverter publishModuleDescriptorConverter,
-                                      IvyFactory ivyFactory,
-                                      IvyDependencyPublisher dependencyPublisher) {
-        this.settingsConverter = settingsConverter;
+    public IvyBackedArtifactPublisher(ModuleDescriptorConverter publishModuleDescriptorConverter,
+                                      IvyContextManager ivyContextManager,
+                                      IvyDependencyPublisher dependencyPublisher,
+                                      IvyModuleDescriptorWriter ivyModuleDescriptorWriter) {
         this.publishModuleDescriptorConverter = publishModuleDescriptorConverter;
-        this.ivyFactory = ivyFactory;
+        this.ivyContextManager = ivyContextManager;
         this.dependencyPublisher = dependencyPublisher;
+        this.ivyModuleDescriptorWriter = ivyModuleDescriptorWriter;
     }
 
-    private Ivy ivyForPublish(List<DependencyResolver> publishResolvers) {
-        return ivyFactory.createIvy(settingsConverter.convertForPublish(publishResolvers));
-    }
+    public void publish(final Iterable<? extends PublicationAwareRepository> repositories, final Module module, final Set<? extends Configuration> configurations, final File descriptor) throws PublishException {
+        ivyContextManager.withIvy(new Action<Ivy>() {
+            public void execute(Ivy ivy) {
+                Set<Configuration> allConfigurations = configurations.iterator().next().getAll();
+                ModuleVersionPublishMetaData publishMetaData = publishModuleDescriptorConverter.convert(allConfigurations, module);
+                if (descriptor != null) {
+                    ModuleDescriptor moduleDescriptor = publishMetaData.getModuleDescriptor();
+                    ivyModuleDescriptorWriter.write(moduleDescriptor, descriptor);
+                }
 
-    public void publish(Iterable<? extends PublicationAwareRepository> repositories, Module module, Set<? extends Configuration> configurations, File descriptor) throws PublishException {
-        List<DependencyResolver> publishResolvers = new ArrayList<DependencyResolver>();
-        for (PublicationAwareRepository repository : repositories) {
-            publishResolvers.add(repository.createPublisher());
-        }
-        Ivy ivy = ivyForPublish(publishResolvers);
-        Set<String> confs = Configurations.getNames(configurations, false);
-        dependencyPublisher.publish(
-                confs,
-                publishResolvers,
-                publishModuleDescriptorConverter.convert(configurations, module),
-                descriptor,
-                ivy.getEventManager());
-    }
+                List<ModuleVersionPublisher> publishResolvers = new ArrayList<ModuleVersionPublisher>();
+                for (PublicationAwareRepository repository : repositories) {
+                    ModuleVersionPublisher publisher = repository.createPublisher();
+                    publisher.setSettings(ivy.getSettings());
+                    publishResolvers.add(publisher);
+                }
 
+                publishMetaData = publishModuleDescriptorConverter.convert(configurations, module);
+                Set<String> confs = Configurations.getNames(configurations, false);
+                dependencyPublisher.publish(
+                        confs,
+                        publishResolvers,
+                        publishMetaData,
+                        descriptor);
+            }
+        });
+    }
 }
diff --git a/subprojects/core-impl/src/main/groovy/org/gradle/api/internal/artifacts/ivyservice/IvyContextManager.java b/subprojects/core-impl/src/main/groovy/org/gradle/api/internal/artifacts/ivyservice/IvyContextManager.java
new file mode 100644
index 0000000..660f384
--- /dev/null
+++ b/subprojects/core-impl/src/main/groovy/org/gradle/api/internal/artifacts/ivyservice/IvyContextManager.java
@@ -0,0 +1,39 @@
+/*
+ * Copyright 2013 the original author or authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY 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 net.jcip.annotations.ThreadSafe;
+import org.apache.ivy.Ivy;
+import org.gradle.api.Action;
+import org.gradle.api.Transformer;
+
+ at ThreadSafe
+public interface IvyContextManager {
+    /**
+     * Executes the given action against an Ivy instance. Sets up the Ivy context before the action and cleans up at the end.
+     *
+     * <p>The Ivy instance of the calling thread is reused if the thread is already executing an action against an Ivy instance.
+     */
+    void withIvy(Action<? super Ivy> action);
+
+    /**
+     * Executes the given action against an Ivy instance and returns the result. Sets up the Ivy context before the action and cleans up at the end.
+     *
+     * <p>The Ivy instance of the calling thread is reused if the thread is already executing an action against an Ivy instance.
+     */
+    <T> T withIvy(Transformer<? extends T, ? super Ivy> action);
+}
diff --git a/subprojects/core-impl/src/main/groovy/org/gradle/api/internal/artifacts/ivyservice/IvyDependencyPublisher.java b/subprojects/core-impl/src/main/groovy/org/gradle/api/internal/artifacts/ivyservice/IvyDependencyPublisher.java
index 650127c..5618332 100644
--- a/subprojects/core-impl/src/main/groovy/org/gradle/api/internal/artifacts/ivyservice/IvyDependencyPublisher.java
+++ b/subprojects/core-impl/src/main/groovy/org/gradle/api/internal/artifacts/ivyservice/IvyDependencyPublisher.java
@@ -15,20 +15,16 @@
  */
 package org.gradle.api.internal.artifacts.ivyservice;
 
-import org.apache.ivy.core.event.EventManager;
-import org.apache.ivy.core.module.descriptor.ModuleDescriptor;
-import org.apache.ivy.plugins.resolver.DependencyResolver;
+import org.gradle.api.internal.artifacts.ModuleVersionPublishMetaData;
+import org.gradle.api.internal.artifacts.ModuleVersionPublisher;
 
 import java.io.File;
 import java.util.List;
 import java.util.Set;
 
-/**
- * @author Hans Dockter
- */
 public interface IvyDependencyPublisher {
     void publish(Set<String> configurations,
-                 List<DependencyResolver> publishResolvers,
-                 ModuleDescriptor moduleDescriptor,
-                 File descriptorDestination, EventManager eventManager);
+                 List<ModuleVersionPublisher> publishResolvers,
+                 ModuleVersionPublishMetaData publishMetaData,
+                 File descriptorDestination);
 }
diff --git a/subprojects/core-impl/src/main/groovy/org/gradle/api/internal/artifacts/ivyservice/IvyFactory.java b/subprojects/core-impl/src/main/groovy/org/gradle/api/internal/artifacts/ivyservice/IvyFactory.java
deleted file mode 100644
index 4dfc079..0000000
--- a/subprojects/core-impl/src/main/groovy/org/gradle/api/internal/artifacts/ivyservice/IvyFactory.java
+++ /dev/null
@@ -1,26 +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.ivyservice;
-
-import org.apache.ivy.Ivy;
-import org.apache.ivy.core.settings.IvySettings;
-
-/**
- * @author Hans Dockter
- */
-public interface IvyFactory {
-    public Ivy createIvy(IvySettings ivySettings);
-}
diff --git a/subprojects/core-impl/src/main/groovy/org/gradle/api/internal/artifacts/ivyservice/IvyLoggingAdaper.java b/subprojects/core-impl/src/main/groovy/org/gradle/api/internal/artifacts/ivyservice/IvyLoggingAdaper.java
index 3daaaff..d072a42 100644
--- a/subprojects/core-impl/src/main/groovy/org/gradle/api/internal/artifacts/ivyservice/IvyLoggingAdaper.java
+++ b/subprojects/core-impl/src/main/groovy/org/gradle/api/internal/artifacts/ivyservice/IvyLoggingAdaper.java
@@ -24,8 +24,6 @@ import org.gradle.api.logging.Logging;
  * logback. This would be bad for embedded usage. We only want one on slf4j. But slf4j has no constants for log levels.
  * As we want to avoid the execution of if statements for each Ivy request, we use Map which delegates Ivy log
  * statements to Sl4j action classes.
- *
- * @author Hans Dockter
  */
 public class IvyLoggingAdaper extends AbstractMessageLogger {
     private final Logger logger = Logging.getLogger(IvyLoggingAdaper.class);
@@ -38,6 +36,22 @@ public class IvyLoggingAdaper extends AbstractMessageLogger {
         log(msg, level);
     }
 
+    /**
+     * Overrides the default implementation, which doesn't delegate to {@link #log(String, int)}.
+     */
+    @Override
+    public void warn(String msg) {
+        logger.warn(msg);
+    }
+
+    /**
+     * Overrides the default implementation, which doesn't delegate to {@link #log(String, int)}.
+     */
+    @Override
+    public void error(String msg) {
+        logger.error(msg);
+    }
+
     public void doProgress() {
     }
 
diff --git a/subprojects/core/src/main/groovy/org/gradle/api/internal/artifacts/ivyservice/IvyModuleDescriptorWriter.java b/subprojects/core-impl/src/main/groovy/org/gradle/api/internal/artifacts/ivyservice/IvyModuleDescriptorWriter.java
similarity index 100%
rename from subprojects/core/src/main/groovy/org/gradle/api/internal/artifacts/ivyservice/IvyModuleDescriptorWriter.java
rename to subprojects/core-impl/src/main/groovy/org/gradle/api/internal/artifacts/ivyservice/IvyModuleDescriptorWriter.java
diff --git a/subprojects/core-impl/src/main/groovy/org/gradle/api/internal/artifacts/ivyservice/IvyResolverBackedModuleVersionPublisher.java b/subprojects/core-impl/src/main/groovy/org/gradle/api/internal/artifacts/ivyservice/IvyResolverBackedModuleVersionPublisher.java
new file mode 100644
index 0000000..0bc8126
--- /dev/null
+++ b/subprojects/core-impl/src/main/groovy/org/gradle/api/internal/artifacts/ivyservice/IvyResolverBackedModuleVersionPublisher.java
@@ -0,0 +1,66 @@
+/*
+ * Copyright 2012 the original author or authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY 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.module.descriptor.Artifact;
+import org.apache.ivy.core.module.id.ModuleRevisionId;
+import org.apache.ivy.core.settings.IvySettings;
+import org.apache.ivy.plugins.resolver.DependencyResolver;
+import org.gradle.api.artifacts.ModuleVersionIdentifier;
+import org.gradle.api.internal.artifacts.ModuleVersionPublishMetaData;
+import org.gradle.api.internal.artifacts.ModuleVersionPublisher;
+
+import java.io.File;
+import java.io.IOException;
+import java.util.Map;
+
+public class IvyResolverBackedModuleVersionPublisher implements ModuleVersionPublisher {
+    private final DependencyResolver resolver;
+
+    public IvyResolverBackedModuleVersionPublisher(DependencyResolver resolver) {
+        this.resolver = resolver;
+    }
+
+    @Override
+    public String toString() {
+        return String.format("repository '%s'", resolver.getName());
+    }
+
+    public void setSettings(IvySettings settings) {
+        settings.addResolver(resolver);
+    }
+
+    public void publish(ModuleVersionPublishMetaData moduleVersion) throws IOException {
+        boolean successfullyPublished = false;
+        try {
+            ModuleVersionIdentifier id = moduleVersion.getId();
+            ModuleRevisionId ivyId = ModuleRevisionId.newInstance(id.getGroup(), id.getName(), id.getVersion());
+            resolver.beginPublishTransaction(ivyId, true);
+            for (Map.Entry<Artifact, File> entry : moduleVersion.getArtifacts().entrySet()) {
+                Artifact artifact = entry.getKey();
+                File artifactFile = entry.getValue();
+                resolver.publish(artifact, artifactFile, true);
+            }
+            resolver.commitPublishTransaction();
+            successfullyPublished = true;
+        } finally {
+            if (!successfullyPublished) {
+                resolver.abortPublishTransaction();
+            }
+        }
+    }
+}
diff --git a/subprojects/core-impl/src/main/groovy/org/gradle/api/internal/artifacts/ivyservice/IvySettingsFactory.java b/subprojects/core-impl/src/main/groovy/org/gradle/api/internal/artifacts/ivyservice/IvySettingsFactory.java
deleted file mode 100644
index 11f49c3..0000000
--- a/subprojects/core-impl/src/main/groovy/org/gradle/api/internal/artifacts/ivyservice/IvySettingsFactory.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.api.internal.artifacts.ivyservice;
-
-import org.apache.ivy.core.settings.IvySettings;
-import org.gradle.api.artifacts.ArtifactRepositoryContainer;
-import org.gradle.internal.Factory;
-
-import java.io.File;
-
-public class IvySettingsFactory implements Factory<IvySettings> {
-    private final ArtifactCacheMetaData cacheMetaData;
-
-    public IvySettingsFactory(ArtifactCacheMetaData cacheMetaData) {
-        this.cacheMetaData = cacheMetaData;
-    }
-
-    public IvySettings create() {
-        IvySettings ivySettings = new IvySettings();
-        ivySettings.setDefaultCache(new File(cacheMetaData.getCacheDir(), "ivy"));
-        ivySettings.setDefaultCacheIvyPattern(ArtifactRepositoryContainer.DEFAULT_CACHE_IVY_PATTERN);
-        ivySettings.setDefaultCacheArtifactPattern(ArtifactRepositoryContainer.DEFAULT_CACHE_ARTIFACT_PATTERN);
-        ivySettings.setVariable("ivy.log.modules.in.use", "false");
-
-        return ivySettings;
-    }
-}
diff --git a/subprojects/core-impl/src/main/groovy/org/gradle/api/internal/artifacts/ivyservice/IvyUtil.java b/subprojects/core-impl/src/main/groovy/org/gradle/api/internal/artifacts/ivyservice/IvyUtil.java
index 856cd82..3ae7cc2 100644
--- a/subprojects/core-impl/src/main/groovy/org/gradle/api/internal/artifacts/ivyservice/IvyUtil.java
+++ b/subprojects/core-impl/src/main/groovy/org/gradle/api/internal/artifacts/ivyservice/IvyUtil.java
@@ -23,9 +23,6 @@ import org.gradle.util.GUtil;
 import java.util.HashMap;
 import java.util.Map;
 
-/**
- * @author Hans Dockter
- */
 public class IvyUtil {
 
     public static ModuleRevisionId createModuleRevisionId(Module module) {
diff --git a/subprojects/core-impl/src/main/groovy/org/gradle/api/internal/artifacts/ivyservice/IvyXmlModuleDescriptorWriter.java b/subprojects/core-impl/src/main/groovy/org/gradle/api/internal/artifacts/ivyservice/IvyXmlModuleDescriptorWriter.java
index 50bcbf7..66bf56c 100644
--- a/subprojects/core-impl/src/main/groovy/org/gradle/api/internal/artifacts/ivyservice/IvyXmlModuleDescriptorWriter.java
+++ b/subprojects/core-impl/src/main/groovy/org/gradle/api/internal/artifacts/ivyservice/IvyXmlModuleDescriptorWriter.java
@@ -20,7 +20,6 @@ import org.apache.ivy.core.IvyPatternHelper;
 import org.apache.ivy.core.module.descriptor.*;
 import org.apache.ivy.core.module.id.ModuleRevisionId;
 import org.apache.ivy.plugins.matcher.MapMatcher;
-import org.apache.ivy.util.Message;
 import org.apache.ivy.util.StringUtils;
 import org.apache.ivy.util.XMLHelper;
 import org.apache.ivy.util.extendable.ExtendableItem;
@@ -28,6 +27,8 @@ import org.gradle.api.Action;
 import org.gradle.api.internal.ErroringAction;
 import org.gradle.api.internal.IoActions;
 import org.gradle.util.TextUtil;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
 
 import java.io.File;
 import java.io.IOException;
@@ -38,6 +39,7 @@ import java.util.Iterator;
 import java.util.Map;
 
 public class IvyXmlModuleDescriptorWriter implements IvyModuleDescriptorWriter {
+    private static final Logger LOGGER = LoggerFactory.getLogger(IvyXmlModuleDescriptorWriter.class);
 
     public static final String IVY_DATE_PATTERN = "yyyyMMddHHmmss";
 
@@ -205,8 +207,7 @@ public class IvyXmlModuleDescriptorWriter implements IvyModuleDescriptorWriter {
                 writer.write("/>");
                 writer.write(TextUtil.getPlatformLineSeparator());
             } else {
-                Message.verbose("ignoring unhandled DependencyDescriptorMediator: "
-                        + mediator.getClass());
+                LOGGER.debug("Ignoring unhandled DependencyDescriptorMediator: {}", mediator.getClass());
             }
         }
     }
diff --git a/subprojects/core-impl/src/main/groovy/org/gradle/api/internal/artifacts/ivyservice/ModuleDescriptorConverter.java b/subprojects/core-impl/src/main/groovy/org/gradle/api/internal/artifacts/ivyservice/ModuleDescriptorConverter.java
new file mode 100644
index 0000000..d2bcb49
--- /dev/null
+++ b/subprojects/core-impl/src/main/groovy/org/gradle/api/internal/artifacts/ivyservice/ModuleDescriptorConverter.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.ivyservice;
+
+import org.gradle.api.artifacts.Configuration;
+import org.gradle.api.artifacts.Module;
+import org.gradle.api.internal.artifacts.ModuleVersionPublishMetaData;
+
+import java.util.Set;
+
+public interface ModuleDescriptorConverter {
+    ModuleVersionPublishMetaData convert(Set<? extends Configuration> configurations, Module module);
+}
diff --git a/subprojects/core-impl/src/main/groovy/org/gradle/api/internal/artifacts/ivyservice/ModuleToModuleVersionResolver.java b/subprojects/core-impl/src/main/groovy/org/gradle/api/internal/artifacts/ivyservice/ModuleToModuleVersionResolver.java
new file mode 100755
index 0000000..c492d04
--- /dev/null
+++ b/subprojects/core-impl/src/main/groovy/org/gradle/api/internal/artifacts/ivyservice/ModuleToModuleVersionResolver.java
@@ -0,0 +1,29 @@
+/*
+ * Copyright 2013 the original author or authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY 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.gradle.api.artifacts.Configuration;
+import org.gradle.api.artifacts.Module;
+
+import java.util.Set;
+
+/**
+ * Resolves a module to the meta-data for a module.
+ */
+public interface ModuleToModuleVersionResolver {
+    void resolve(Module module, Set<? extends Configuration> configurations, BuildableModuleVersionResolveResult result);
+}
diff --git a/subprojects/core-impl/src/main/groovy/org/gradle/api/internal/artifacts/ivyservice/ModuleVersionResolveException.java b/subprojects/core-impl/src/main/groovy/org/gradle/api/internal/artifacts/ivyservice/ModuleVersionResolveException.java
index a6a78c9..b991555 100644
--- a/subprojects/core-impl/src/main/groovy/org/gradle/api/internal/artifacts/ivyservice/ModuleVersionResolveException.java
+++ b/subprojects/core-impl/src/main/groovy/org/gradle/api/internal/artifacts/ivyservice/ModuleVersionResolveException.java
@@ -30,7 +30,7 @@ import java.util.List;
 
 @Contextual
 public class ModuleVersionResolveException extends AbstractMultiCauseException {
-    private final List<List<ModuleRevisionId>> paths = new ArrayList<List<ModuleRevisionId>>();
+    private final List<List<ModuleVersionIdentifier>> paths = new ArrayList<List<ModuleVersionIdentifier>>();
     private final String messageFormat;
     private final ModuleVersionSelector selector;
 
@@ -81,7 +81,7 @@ public class ModuleVersionResolveException extends AbstractMultiCauseException {
     /**
      * Creates a copy of this exception, with the given incoming paths.
      */
-    public ModuleVersionResolveException withIncomingPaths(Collection<? extends List<ModuleRevisionId>> paths) {
+    public ModuleVersionResolveException withIncomingPaths(Collection<? extends List<ModuleVersionIdentifier>> paths) {
         ModuleVersionResolveException copy = createCopy();
         copy.paths.addAll(paths);
         copy.initCauses(getCauses());
@@ -96,7 +96,7 @@ public class ModuleVersionResolveException extends AbstractMultiCauseException {
         }
         Formatter formatter = new Formatter();
         formatter.format("%s%nRequired by:", super.getMessage());
-        for (List<ModuleRevisionId> path : paths) {
+        for (List<ModuleVersionIdentifier> path : paths) {
             formatter.format("%n    %s", toString(path.get(0)));
             for (int i = 1; i < path.size(); i++) {
                 formatter.format(" > %s", toString(path.get(i)));
@@ -105,8 +105,8 @@ public class ModuleVersionResolveException extends AbstractMultiCauseException {
         return formatter.toString();
     }
 
-    private String toString(ModuleRevisionId moduleRevisionId) {
-        return String.format("%s:%s:%s", moduleRevisionId.getOrganisation(), moduleRevisionId.getName(), moduleRevisionId.getRevision());
+    private String toString(ModuleVersionIdentifier identifier) {
+        return identifier.toString();
     }
 
     protected ModuleVersionResolveException createCopy() {
diff --git a/subprojects/core-impl/src/main/groovy/org/gradle/api/internal/artifacts/ivyservice/ResolvedArtifactFactory.java b/subprojects/core-impl/src/main/groovy/org/gradle/api/internal/artifacts/ivyservice/ResolvedArtifactFactory.java
index 5b5b951..4f49714 100644
--- a/subprojects/core-impl/src/main/groovy/org/gradle/api/internal/artifacts/ivyservice/ResolvedArtifactFactory.java
+++ b/subprojects/core-impl/src/main/groovy/org/gradle/api/internal/artifacts/ivyservice/ResolvedArtifactFactory.java
@@ -15,32 +15,38 @@
  */
 package org.gradle.api.internal.artifacts.ivyservice;
 
+import org.apache.ivy.Ivy;
 import org.apache.ivy.core.module.descriptor.Artifact;
-import org.gradle.api.artifacts.ResolvedArtifact;
-import org.gradle.api.artifacts.ResolvedDependency;
-import org.gradle.api.internal.artifacts.DefaultResolvedArtifact;
+import org.gradle.api.Transformer;
 import org.gradle.internal.Factory;
 
 import java.io.File;
 
+//TODO SF rename
 public class ResolvedArtifactFactory {
     private final CacheLockingManager lockingManager;
+    private final IvyContextManager ivyContextManager;
 
-    public ResolvedArtifactFactory(CacheLockingManager lockingManager) {
+    public ResolvedArtifactFactory(CacheLockingManager lockingManager, IvyContextManager ivyContextManager) {
         this.lockingManager = lockingManager;
+        this.ivyContextManager = ivyContextManager;
     }
 
-    public ResolvedArtifact create(ResolvedDependency owner, final Artifact artifact, final ArtifactResolver resolver) {
-        return new DefaultResolvedArtifact(owner, artifact, new Factory<File>() {
+    public Factory<File> artifactSource(final Artifact artifact, final ArtifactResolver resolver) {
+        return new Factory<File>() {
             public File create() {
-                return lockingManager.useCache(String.format("download %s", artifact), new Factory<File>() {
+                return lockingManager.useCache(String.format("resolve %s", artifact), new Factory<File>() {
                     public File create() {
-                        DefaultBuildableArtifactResolveResult result = new DefaultBuildableArtifactResolveResult();
-                        resolver.resolve(artifact, result);
-                        return result.getFile();
+                        return ivyContextManager.withIvy(new Transformer<File, Ivy>() {
+                            public File transform(Ivy original) {
+                                DefaultBuildableArtifactResolveResult result = new DefaultBuildableArtifactResolveResult();
+                                resolver.resolve(artifact, result);
+                                return result.getFile();
+                            }
+                        });
                     }
                 });
             }
-        });
+        };
     }
 }
diff --git a/subprojects/core-impl/src/main/groovy/org/gradle/api/internal/artifacts/ivyservice/ResolvedConfigurationBuilder.java b/subprojects/core-impl/src/main/groovy/org/gradle/api/internal/artifacts/ivyservice/ResolvedConfigurationBuilder.java
deleted file mode 100644
index 1ecee6c..0000000
--- a/subprojects/core-impl/src/main/groovy/org/gradle/api/internal/artifacts/ivyservice/ResolvedConfigurationBuilder.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.api.internal.artifacts.ivyservice;
-
-import org.gradle.api.artifacts.ModuleDependency;
-import org.gradle.api.artifacts.ResolvedArtifact;
-import org.gradle.api.artifacts.ResolvedDependency;
-import org.gradle.api.artifacts.UnresolvedDependency;
-
-public interface ResolvedConfigurationBuilder {
-    void addArtifact(ResolvedArtifact artifact);
-
-    void addFirstLevelDependency(ModuleDependency moduleDependency, ResolvedDependency dependency);
-
-    void addUnresolvedDependency(UnresolvedDependency unresolvedDependency);
-
-    ResolvedDependency getRoot();
-}
diff --git a/subprojects/core-impl/src/main/groovy/org/gradle/api/internal/artifacts/ivyservice/SettingsConverter.java b/subprojects/core-impl/src/main/groovy/org/gradle/api/internal/artifacts/ivyservice/SettingsConverter.java
deleted file mode 100644
index 877ffde..0000000
--- a/subprojects/core-impl/src/main/groovy/org/gradle/api/internal/artifacts/ivyservice/SettingsConverter.java
+++ /dev/null
@@ -1,34 +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.ivyservice;
-
-import org.apache.ivy.core.settings.IvySettings;
-import org.apache.ivy.plugins.resolver.DependencyResolver;
-
-import java.util.List;
-
-/**
- * @author Hans Dockter
- */
-public interface SettingsConverter {
-    String LOOPBACK_RESOLVER_NAME = "main";
-
-    IvySettings convertForPublish(List<DependencyResolver> publishResolvers);
-
-    IvySettings convertForResolve(DependencyResolver defaultResolver);
-
-    IvySettings getForResolve();
-}
diff --git a/subprojects/core-impl/src/main/groovy/org/gradle/api/internal/artifacts/ivyservice/ShortcircuitEmptyConfigsArtifactDependencyResolver.java b/subprojects/core-impl/src/main/groovy/org/gradle/api/internal/artifacts/ivyservice/ShortcircuitEmptyConfigsArtifactDependencyResolver.java
index f7820c0..622d18b 100644
--- a/subprojects/core-impl/src/main/groovy/org/gradle/api/internal/artifacts/ivyservice/ShortcircuitEmptyConfigsArtifactDependencyResolver.java
+++ b/subprojects/core-impl/src/main/groovy/org/gradle/api/internal/artifacts/ivyservice/ShortcircuitEmptyConfigsArtifactDependencyResolver.java
@@ -16,13 +16,13 @@
 package org.gradle.api.internal.artifacts.ivyservice;
 
 import org.gradle.api.artifacts.*;
+import org.gradle.api.artifacts.result.ResolutionResult;
 import org.gradle.api.internal.artifacts.ArtifactDependencyResolver;
 import org.gradle.api.internal.artifacts.DefaultModuleVersionIdentifier;
 import org.gradle.api.internal.artifacts.ResolverResults;
 import org.gradle.api.internal.artifacts.configurations.ConfigurationInternal;
 import org.gradle.api.internal.artifacts.ivyservice.resolveengine.result.ResolutionResultBuilder;
 import org.gradle.api.internal.artifacts.repositories.ResolutionAwareRepository;
-import org.gradle.api.internal.artifacts.result.DefaultResolutionResult;
 import org.gradle.api.specs.Spec;
 
 import java.io.File;
@@ -40,7 +40,7 @@ public class ShortcircuitEmptyConfigsArtifactDependencyResolver implements Artif
     public ResolverResults resolve(ConfigurationInternal configuration, List<? extends ResolutionAwareRepository> repositories) throws ResolveException {
         if (configuration.getAllDependencies().isEmpty()) {
             ModuleVersionIdentifier id = DefaultModuleVersionIdentifier.newId(configuration.getModule());
-            DefaultResolutionResult emptyResult = new ResolutionResultBuilder().start(id).getResult();
+            ResolutionResult emptyResult = new ResolutionResultBuilder().start(id).complete();
             return new ResolverResults(new EmptyResolvedConfiguration(), emptyResult);
         }
         return dependencyResolver.resolve(configuration, repositories);
diff --git a/subprojects/core-impl/src/main/groovy/org/gradle/api/internal/artifacts/ivyservice/SubstitutedModuleVersionIdResolveResult.java b/subprojects/core-impl/src/main/groovy/org/gradle/api/internal/artifacts/ivyservice/SubstitutedModuleVersionIdResolveResult.java
index 8971b89..9e49c53 100644
--- a/subprojects/core-impl/src/main/groovy/org/gradle/api/internal/artifacts/ivyservice/SubstitutedModuleVersionIdResolveResult.java
+++ b/subprojects/core-impl/src/main/groovy/org/gradle/api/internal/artifacts/ivyservice/SubstitutedModuleVersionIdResolveResult.java
@@ -19,9 +19,6 @@ package org.gradle.api.internal.artifacts.ivyservice;
 import org.gradle.api.artifacts.ModuleVersionIdentifier;
 import org.gradle.api.artifacts.result.ModuleVersionSelectionReason;
 
-/**
- * by Szczepan Faber, created at: 8/29/12
- */
 class SubstitutedModuleVersionIdResolveResult implements ModuleVersionIdResolveResult {
 
     final ModuleVersionIdResolveResult result;
diff --git a/subprojects/core-impl/src/main/groovy/org/gradle/api/internal/artifacts/ivyservice/clientmodule/ClientModuleResolver.java b/subprojects/core-impl/src/main/groovy/org/gradle/api/internal/artifacts/ivyservice/clientmodule/ClientModuleResolver.java
index cbf202d..999b705 100644
--- a/subprojects/core-impl/src/main/groovy/org/gradle/api/internal/artifacts/ivyservice/clientmodule/ClientModuleResolver.java
+++ b/subprojects/core-impl/src/main/groovy/org/gradle/api/internal/artifacts/ivyservice/clientmodule/ClientModuleResolver.java
@@ -1,5 +1,5 @@
 /*
- * Copyright 2009 the original author or authors.
+ * Copyright 2013 the original author 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,17 +19,14 @@ package org.gradle.api.internal.artifacts.ivyservice.clientmodule;
 import org.apache.ivy.core.module.descriptor.DependencyDescriptor;
 import org.apache.ivy.core.module.descriptor.ModuleDescriptor;
 import org.gradle.api.internal.artifacts.ivyservice.BuildableModuleVersionResolveResult;
-import org.gradle.api.internal.artifacts.ivyservice.DependencyToModuleResolver;
+import org.gradle.api.internal.artifacts.ivyservice.DependencyToModuleVersionResolver;
 import org.gradle.api.internal.artifacts.ivyservice.ivyresolve.DependencyMetaData;
 import org.gradle.api.internal.artifacts.ivyservice.moduleconverter.dependencies.ClientModuleDependencyDescriptor;
 
-/**
- * @author Hans Dockter
- */
-public class ClientModuleResolver implements DependencyToModuleResolver {
-    private final DependencyToModuleResolver resolver;
+public class ClientModuleResolver implements DependencyToModuleVersionResolver {
+    private final DependencyToModuleVersionResolver resolver;
 
-    public ClientModuleResolver(DependencyToModuleResolver resolver) {
+    public ClientModuleResolver(DependencyToModuleVersionResolver resolver) {
         this.resolver = resolver;
     }
 
diff --git a/subprojects/core-impl/src/main/groovy/org/gradle/api/internal/artifacts/ivyservice/dynamicversions/DefaultCachedModuleResolution.java b/subprojects/core-impl/src/main/groovy/org/gradle/api/internal/artifacts/ivyservice/dynamicversions/DefaultCachedModuleResolution.java
index 2a3f3da..661e4d1 100644
--- a/subprojects/core-impl/src/main/groovy/org/gradle/api/internal/artifacts/ivyservice/dynamicversions/DefaultCachedModuleResolution.java
+++ b/subprojects/core-impl/src/main/groovy/org/gradle/api/internal/artifacts/ivyservice/dynamicversions/DefaultCachedModuleResolution.java
@@ -15,18 +15,17 @@
  */
 package org.gradle.api.internal.artifacts.ivyservice.dynamicversions;
 
-import org.apache.ivy.core.module.id.ModuleRevisionId;
 import org.gradle.api.artifacts.ModuleVersionIdentifier;
-import org.gradle.api.internal.artifacts.DefaultModuleVersionIdentifier;
+import org.gradle.api.artifacts.ModuleVersionSelector;
 import org.gradle.internal.TimeProvider;
 
 class DefaultCachedModuleResolution implements ModuleResolutionCache.CachedModuleResolution {
-    private final ModuleVersionIdentifier requestedVersion;
+    private final ModuleVersionSelector requestedVersion;
     private final ModuleVersionIdentifier resolvedVersion;
     private final long ageMillis;
 
-    public DefaultCachedModuleResolution(ModuleRevisionId requestedVersion, ModuleResolutionCacheEntry entry, TimeProvider timeProvider) {
-        this.requestedVersion = new DefaultModuleVersionIdentifier(requestedVersion.getOrganisation(), requestedVersion.getName(), requestedVersion.getRevision());
+    public DefaultCachedModuleResolution(ModuleVersionSelector requestedVersion, ModuleResolutionCacheEntry entry, TimeProvider timeProvider) {
+        this.requestedVersion = requestedVersion;
         this.resolvedVersion = entry.moduleVersionIdentifier;
         ageMillis = timeProvider.getCurrentTime() - entry.createTimestamp;
     }
@@ -40,6 +39,6 @@ class DefaultCachedModuleResolution implements ModuleResolutionCache.CachedModul
     }
 
     public boolean isDynamicVersion() {
-        return !requestedVersion.equals(resolvedVersion);
+        return !requestedVersion.matchesStrictly(resolvedVersion);
     }
 }
diff --git a/subprojects/core-impl/src/main/groovy/org/gradle/api/internal/artifacts/ivyservice/dynamicversions/DefaultResolvedModuleVersion.java b/subprojects/core-impl/src/main/groovy/org/gradle/api/internal/artifacts/ivyservice/dynamicversions/DefaultResolvedModuleVersion.java
index 706f4a1..30068d4 100644
--- a/subprojects/core-impl/src/main/groovy/org/gradle/api/internal/artifacts/ivyservice/dynamicversions/DefaultResolvedModuleVersion.java
+++ b/subprojects/core-impl/src/main/groovy/org/gradle/api/internal/artifacts/ivyservice/dynamicversions/DefaultResolvedModuleVersion.java
@@ -15,19 +15,17 @@
  */
 package org.gradle.api.internal.artifacts.ivyservice.dynamicversions;
 
-import org.apache.ivy.core.module.id.ModuleRevisionId;
 import org.gradle.api.artifacts.ModuleVersionIdentifier;
 import org.gradle.api.artifacts.ResolvedModuleVersion;
-import org.gradle.api.internal.artifacts.DefaultModuleVersionIdentifier;
 
 public class DefaultResolvedModuleVersion implements ResolvedModuleVersion {
-    private final ModuleRevisionId moduleRevisionId;
+    private final ModuleVersionIdentifier identifier;
 
-    public DefaultResolvedModuleVersion(ModuleRevisionId moduleRevisionId) {
-        this.moduleRevisionId = moduleRevisionId;
+    public DefaultResolvedModuleVersion(ModuleVersionIdentifier identifier) {
+        this.identifier = identifier;
     }
 
     public ModuleVersionIdentifier getId() {
-        return new DefaultModuleVersionIdentifier(moduleRevisionId.getOrganisation(), moduleRevisionId.getName(), moduleRevisionId.getRevision());
+        return identifier;
     }
 }
diff --git a/subprojects/core-impl/src/main/groovy/org/gradle/api/internal/artifacts/ivyservice/dynamicversions/ModuleResolutionCache.java b/subprojects/core-impl/src/main/groovy/org/gradle/api/internal/artifacts/ivyservice/dynamicversions/ModuleResolutionCache.java
index ed9717f..b5eb4a6 100644
--- a/subprojects/core-impl/src/main/groovy/org/gradle/api/internal/artifacts/ivyservice/dynamicversions/ModuleResolutionCache.java
+++ b/subprojects/core-impl/src/main/groovy/org/gradle/api/internal/artifacts/ivyservice/dynamicversions/ModuleResolutionCache.java
@@ -15,15 +15,15 @@
  */
 package org.gradle.api.internal.artifacts.ivyservice.dynamicversions;
 
-import org.apache.ivy.core.module.id.ModuleRevisionId;
 import org.gradle.api.artifacts.ModuleVersionIdentifier;
+import org.gradle.api.artifacts.ModuleVersionSelector;
 import org.gradle.api.internal.artifacts.ivyservice.ivyresolve.ModuleVersionRepository;
 
 public interface ModuleResolutionCache {
 
-    void cacheModuleResolution(ModuleVersionRepository repository, ModuleRevisionId dynamicVersion, ModuleVersionIdentifier moduleVersionIdentifier);
+    void cacheModuleResolution(ModuleVersionRepository repository, ModuleVersionSelector requestedVersion, ModuleVersionIdentifier resolvedVersion);
 
-    CachedModuleResolution getCachedModuleResolution(ModuleVersionRepository repository, ModuleRevisionId dynamicVersion);
+    CachedModuleResolution getCachedModuleResolution(ModuleVersionRepository repository, ModuleVersionSelector requestedVersion);
 
     interface CachedModuleResolution {
         ModuleVersionIdentifier getResolvedVersion();
diff --git a/subprojects/core-impl/src/main/groovy/org/gradle/api/internal/artifacts/ivyservice/dynamicversions/SingleFileBackedModuleResolutionCache.java b/subprojects/core-impl/src/main/groovy/org/gradle/api/internal/artifacts/ivyservice/dynamicversions/SingleFileBackedModuleResolutionCache.java
index 5aa0bac..064ad09 100644
--- a/subprojects/core-impl/src/main/groovy/org/gradle/api/internal/artifacts/ivyservice/dynamicversions/SingleFileBackedModuleResolutionCache.java
+++ b/subprojects/core-impl/src/main/groovy/org/gradle/api/internal/artifacts/ivyservice/dynamicversions/SingleFileBackedModuleResolutionCache.java
@@ -15,19 +15,22 @@
  */
 package org.gradle.api.internal.artifacts.ivyservice.dynamicversions;
 
-import org.apache.ivy.core.module.id.ModuleRevisionId;
 import org.gradle.api.artifacts.ModuleVersionIdentifier;
+import org.gradle.api.artifacts.ModuleVersionSelector;
+import org.gradle.api.internal.artifacts.DefaultModuleVersionSelector;
 import org.gradle.api.internal.artifacts.ModuleVersionIdentifierSerializer;
 import org.gradle.api.internal.artifacts.ivyservice.ArtifactCacheMetaData;
 import org.gradle.api.internal.artifacts.ivyservice.CacheLockingManager;
 import org.gradle.api.internal.artifacts.ivyservice.ivyresolve.ModuleVersionRepository;
 import org.gradle.cache.PersistentIndexedCache;
 import org.gradle.internal.TimeProvider;
-import org.gradle.messaging.serialize.DataStreamBackedSerializer;
+import org.gradle.messaging.serialize.Decoder;
+import org.gradle.messaging.serialize.Encoder;
+import org.gradle.messaging.serialize.Serializer;
 import org.slf4j.Logger;
 import org.slf4j.LoggerFactory;
 
-import java.io.*;
+import java.io.File;
 
 public class SingleFileBackedModuleResolutionCache implements ModuleResolutionCache {
     private static final Logger LOGGER = LoggerFactory.getLogger(SingleFileBackedModuleResolutionCache.class);
@@ -55,25 +58,25 @@ public class SingleFileBackedModuleResolutionCache implements ModuleResolutionCa
         return cacheLockingManager.createCache(dynamicRevisionsFile, new RevisionKeySerializer(), new ModuleResolutionCacheEntrySerializer());
     }
 
-    public void cacheModuleResolution(ModuleVersionRepository repository, ModuleRevisionId requestedVersion, ModuleVersionIdentifier moduleVersionIdentifier) {
-        if (requestedVersion.equals(moduleVersionIdentifier)) {
+    public void cacheModuleResolution(ModuleVersionRepository repository, ModuleVersionSelector requestedVersion, ModuleVersionIdentifier resolvedVersion) {
+        if (requestedVersion.matchesStrictly(resolvedVersion)) {
             return;
         }
 
-        LOGGER.debug("Caching resolved revision in dynamic revision cache: Will use '{}' for '{}'", moduleVersionIdentifier, requestedVersion);
-        getCache().put(createKey(repository, requestedVersion), createEntry(moduleVersionIdentifier));
+        LOGGER.debug("Caching resolved revision in dynamic revision cache: Will use '{}' for '{}'", resolvedVersion, requestedVersion);
+        getCache().put(createKey(repository, requestedVersion), createEntry(resolvedVersion));
     }
 
-    public CachedModuleResolution getCachedModuleResolution(ModuleVersionRepository repository, ModuleRevisionId moduleId) {
-        ModuleResolutionCacheEntry moduleResolutionCacheEntry = getCache().get(createKey(repository, moduleId));
+    public CachedModuleResolution getCachedModuleResolution(ModuleVersionRepository repository, ModuleVersionSelector requestedVersion) {
+        ModuleResolutionCacheEntry moduleResolutionCacheEntry = getCache().get(createKey(repository, requestedVersion));
         if (moduleResolutionCacheEntry == null) {
             return null;
         }
-        return new DefaultCachedModuleResolution(moduleId, moduleResolutionCacheEntry, timeProvider);
+        return new DefaultCachedModuleResolution(requestedVersion, moduleResolutionCacheEntry, timeProvider);
     }
 
-    private RevisionKey createKey(ModuleVersionRepository repository, ModuleRevisionId revisionId) {
-        return new RevisionKey(repository.getId(), revisionId.encodeToString());
+    private RevisionKey createKey(ModuleVersionRepository repository, ModuleVersionSelector requestedVersion) {
+        return new RevisionKey(repository.getId(), requestedVersion);
     }
 
     private ModuleResolutionCacheEntry createEntry(ModuleVersionIdentifier moduleVersionIdentifier) {
@@ -82,11 +85,11 @@ public class SingleFileBackedModuleResolutionCache implements ModuleResolutionCa
 
     private static class RevisionKey {
         private final String repositoryId;
-        private final String revisionId;
+        private final ModuleVersionSelector requestedVersion;
 
-        private RevisionKey(String repositoryId, String revisionId) {
+        private RevisionKey(String repositoryId, ModuleVersionSelector requestedVersion) {
             this.repositoryId = repositoryId;
-            this.revisionId = revisionId;
+            this.requestedVersion = requestedVersion;
         }
 
         @Override
@@ -95,43 +98,43 @@ public class SingleFileBackedModuleResolutionCache implements ModuleResolutionCa
                 return false;
             }
             RevisionKey other = (RevisionKey) o;
-            return repositoryId.equals(other.repositoryId) && revisionId.equals(other.revisionId);
+            return repositoryId.equals(other.repositoryId) && requestedVersion.equals(other.requestedVersion);
         }
 
         @Override
         public int hashCode() {
-            return repositoryId.hashCode() ^ revisionId.hashCode();
+            return repositoryId.hashCode() ^ requestedVersion.hashCode();
         }
     }
 
-    private static class RevisionKeySerializer extends DataStreamBackedSerializer<RevisionKey> {
-        @Override
-        public void write(DataOutput dataOutput, RevisionKey value) throws IOException {
-            dataOutput.writeUTF(value.repositoryId);
-            dataOutput.writeUTF(value.revisionId);
+    private static class RevisionKeySerializer implements Serializer<RevisionKey> {
+        public void write(Encoder encoder, RevisionKey value) throws Exception {
+            encoder.writeString(value.repositoryId);
+            encoder.writeString(value.requestedVersion.getGroup());
+            encoder.writeString(value.requestedVersion.getName());
+            encoder.writeString(value.requestedVersion.getVersion());
         }
 
-        @Override
-        public RevisionKey read(DataInput dataInput) throws IOException {
-            String resolverId = dataInput.readUTF();
-            String revisionId = dataInput.readUTF();
-            return new RevisionKey(resolverId, revisionId);
+        public RevisionKey read(Decoder decoder) throws Exception {
+            String resolverId = decoder.readString();
+            String group = decoder.readString();
+            String module = decoder.readString();
+            String version = decoder.readString();
+            return new RevisionKey(resolverId, DefaultModuleVersionSelector.newSelector(group, module, version));
         }
     }
 
-    private static class ModuleResolutionCacheEntrySerializer extends DataStreamBackedSerializer<ModuleResolutionCacheEntry> {
+    private static class ModuleResolutionCacheEntrySerializer implements Serializer<ModuleResolutionCacheEntry> {
         private final ModuleVersionIdentifierSerializer identifierSerializer = new ModuleVersionIdentifierSerializer();
 
-        @Override
-        public void write(DataOutput dataOutput, ModuleResolutionCacheEntry value) throws IOException {
-            identifierSerializer.write(dataOutput, value.moduleVersionIdentifier);
-            dataOutput.writeLong(value.createTimestamp);
+        public void write(Encoder encoder, ModuleResolutionCacheEntry value) throws Exception {
+            identifierSerializer.write(encoder, value.moduleVersionIdentifier);
+            encoder.writeLong(value.createTimestamp);
         }
 
-        @Override
-        public ModuleResolutionCacheEntry read(DataInput dataInput) throws IOException {
-            ModuleVersionIdentifier identifier = identifierSerializer.read(dataInput);
-            long createTimestamp = dataInput.readLong();
+        public ModuleResolutionCacheEntry read(Decoder decoder) throws Exception {
+            ModuleVersionIdentifier identifier = identifierSerializer.read(decoder);
+            long createTimestamp = decoder.readLong();
             return new ModuleResolutionCacheEntry(identifier, createTimestamp);
         }
     }
diff --git a/subprojects/core-impl/src/main/groovy/org/gradle/api/internal/artifacts/ivyservice/ivyresolve/AbstractDependencyResolverAdapter.java b/subprojects/core-impl/src/main/groovy/org/gradle/api/internal/artifacts/ivyservice/ivyresolve/AbstractDependencyResolverAdapter.java
deleted file mode 100644
index 9280823..0000000
--- a/subprojects/core-impl/src/main/groovy/org/gradle/api/internal/artifacts/ivyservice/ivyresolve/AbstractDependencyResolverAdapter.java
+++ /dev/null
@@ -1,55 +0,0 @@
-/*
- * Copyright 2012 the original author or authors.
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- *      http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY 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.ivyresolve;
-
-import org.apache.ivy.core.settings.IvySettings;
-import org.apache.ivy.plugins.resolver.DependencyResolver;
-import org.gradle.api.internal.artifacts.repositories.cachemanager.LocalFileRepositoryCacheManager;
-
-public abstract class AbstractDependencyResolverAdapter implements IvyAwareModuleVersionRepository {
-    private final DependencyResolverIdentifier identifier;
-    protected final DependencyResolver resolver;
-
-    public AbstractDependencyResolverAdapter(DependencyResolver resolver) {
-        this.identifier = new DependencyResolverIdentifier(resolver);
-        this.resolver = resolver;
-    }
-
-    public String getId() {
-        return identifier.getUniqueId();
-    }
-
-    public String getName() {
-        return identifier.getName();
-    }
-
-    @Override
-    public String toString() {
-        return String.format("Repository '%s'", resolver.getName());
-    }
-
-    public void setSettings(IvySettings settings) {
-        settings.addResolver(resolver);
-    }
-
-    public boolean isDynamicResolveMode() {
-        return false;
-    }
-
-    public boolean isLocal() {
-        return resolver.getRepositoryCacheManager() instanceof LocalFileRepositoryCacheManager;
-    }
-}
diff --git a/subprojects/core-impl/src/main/groovy/org/gradle/api/internal/artifacts/ivyservice/ivyresolve/ArtifactNotFoundException.java b/subprojects/core-impl/src/main/groovy/org/gradle/api/internal/artifacts/ivyservice/ivyresolve/ArtifactNotFoundException.java
index 902334b..76ed48b 100644
--- a/subprojects/core-impl/src/main/groovy/org/gradle/api/internal/artifacts/ivyservice/ivyresolve/ArtifactNotFoundException.java
+++ b/subprojects/core-impl/src/main/groovy/org/gradle/api/internal/artifacts/ivyservice/ivyresolve/ArtifactNotFoundException.java
@@ -15,14 +15,14 @@
  */
 package org.gradle.api.internal.artifacts.ivyservice.ivyresolve;
 
-import org.apache.ivy.core.module.descriptor.Artifact;
+import org.gradle.api.artifacts.ArtifactIdentifier;
 
 public class ArtifactNotFoundException extends ArtifactResolveException {
-    public ArtifactNotFoundException(Artifact artifact) {
+    public ArtifactNotFoundException(ArtifactIdentifier artifact) {
         super(format(artifact));
     }
 
-    private static String format(Artifact artifact) {
+    private static String format(ArtifactIdentifier artifact) {
         StringBuilder builder = new StringBuilder();
         builder.append("Artifact '");
         formatTo(artifact, builder);
diff --git a/subprojects/core-impl/src/main/groovy/org/gradle/api/internal/artifacts/ivyservice/ivyresolve/ArtifactResolveException.java b/subprojects/core-impl/src/main/groovy/org/gradle/api/internal/artifacts/ivyservice/ivyresolve/ArtifactResolveException.java
index 778e9a7..5e36a52 100644
--- a/subprojects/core-impl/src/main/groovy/org/gradle/api/internal/artifacts/ivyservice/ivyresolve/ArtifactResolveException.java
+++ b/subprojects/core-impl/src/main/groovy/org/gradle/api/internal/artifacts/ivyservice/ivyresolve/ArtifactResolveException.java
@@ -16,9 +16,11 @@
 package org.gradle.api.internal.artifacts.ivyservice.ivyresolve;
 
 import org.apache.ivy.core.module.descriptor.Artifact;
-import org.apache.ivy.core.module.id.ModuleRevisionId;
 import org.gradle.api.GradleException;
+import org.gradle.api.artifacts.ArtifactIdentifier;
+import org.gradle.api.artifacts.ModuleVersionIdentifier;
 import org.gradle.api.internal.Contextual;
+import org.gradle.api.internal.artifacts.DefaultArtifactIdentifier;
 import org.gradle.util.GUtil;
 
 @Contextual
@@ -28,14 +30,18 @@ public class ArtifactResolveException extends GradleException {
     }
 
     public ArtifactResolveException(Artifact artifact, Throwable cause) {
+        this(new DefaultArtifactIdentifier(artifact), cause);
+    }
+
+    public ArtifactResolveException(ArtifactIdentifier artifact, Throwable cause) {
         super(format(artifact, ""), cause);
     }
 
-    public ArtifactResolveException(Artifact artifact, String message) {
+    public ArtifactResolveException(ArtifactIdentifier artifact, String message) {
         super(format(artifact, message));
     }
 
-    private static String format(Artifact artifact, String message) {
+    private static String format(ArtifactIdentifier artifact, String message) {
         StringBuilder builder = new StringBuilder();
         builder.append("Could not download artifact '");
         formatTo(artifact, builder);
@@ -47,15 +53,15 @@ public class ArtifactResolveException extends GradleException {
         return builder.toString();
     }
 
-    protected static void formatTo(Artifact artifact, StringBuilder builder) {
-        ModuleRevisionId moduleRevisionId = artifact.getModuleRevisionId();
-        builder.append(moduleRevisionId.getOrganisation())
-                .append(":").append(moduleRevisionId.getName())
-                .append(":").append(moduleRevisionId.getRevision());
-        String classifier = artifact.getExtraAttribute("classifier");
+    protected static void formatTo(ArtifactIdentifier artifact, StringBuilder builder) {
+        ModuleVersionIdentifier moduleVersion = artifact.getModuleVersionIdentifier();
+        builder.append(moduleVersion.getGroup())
+                .append(":").append(moduleVersion.getName())
+                .append(":").append(moduleVersion.getVersion());
+        String classifier = artifact.getClassifier();
         if (GUtil.isTrue(classifier)) {
             builder.append(":").append(classifier);
         }
-        builder.append("@").append(artifact.getExt());
+        builder.append("@").append(artifact.getExtension());
     }
 }
diff --git a/subprojects/core-impl/src/main/groovy/org/gradle/api/internal/artifacts/ivyservice/ivyresolve/BuildableModuleVersionMetaData.java b/subprojects/core-impl/src/main/groovy/org/gradle/api/internal/artifacts/ivyservice/ivyresolve/BuildableModuleVersionMetaData.java
deleted file mode 100644
index f731d7d..0000000
--- a/subprojects/core-impl/src/main/groovy/org/gradle/api/internal/artifacts/ivyservice/ivyresolve/BuildableModuleVersionMetaData.java
+++ /dev/null
@@ -1,78 +0,0 @@
-/*
- * Copyright 2012 the original author or authors.
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- *      http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY 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.ivyresolve;
-
-import org.apache.ivy.core.module.descriptor.ModuleDescriptor;
-import org.gradle.api.Nullable;
-import org.gradle.api.artifacts.ModuleVersionIdentifier;
-import org.gradle.api.internal.artifacts.ivyservice.ModuleVersionResolveException;
-
-/**
- * The result of attempting to resolve a dependency descriptor to the meta-data for a module version.
- *
- * TODO - detach this from ModuleVersionMetaData, so that it has-a instead of is-a.
- */
-public interface BuildableModuleVersionMetaData extends ModuleVersionMetaData {
-    enum State {
-        Resolved, Missing, Failed, ProbablyMissing, Unknown
-    }
-
-    /**
-     * Returns the current state of this descriptor.
-     */
-    State getState();
-
-    @Nullable
-    ModuleVersionResolveException getFailure();
-
-    /**
-     * Marks the module version as resolved, with the given meta-data.
-     */
-    void resolved(ModuleDescriptor descriptor, boolean changing, ModuleSource moduleSource);
-
-    /**
-     * Marks the module version as resolved, with the given meta-data.
-     */
-    void resolved(ModuleVersionIdentifier id, ModuleDescriptor descriptor, boolean changing, ModuleSource moduleSource);
-
-    /**
-     * Marks the resolve as failed with the given exception.
-     */
-    void failed(ModuleVersionResolveException failure);
-
-    /**
-     * Marks the module version as definitely missing.
-     */
-    void missing();
-
-    /**
-     * Marks the module version as probably missing.
-     */
-    void probablyMissing();
-
-    /**
-     * The repository-specific source for this module version.
-     */
-    public ModuleSource getModuleSource();
-
-    void setModuleSource(ModuleSource moduleSource);
-
-    /**
-     * Replaces the dependencies of this module version.
-     */
-    void setDependencies(Iterable<? extends DependencyMetaData> dependencies);
-}
diff --git a/subprojects/core-impl/src/main/groovy/org/gradle/api/internal/artifacts/ivyservice/ivyresolve/BuildableModuleVersionMetaDataResolveResult.java b/subprojects/core-impl/src/main/groovy/org/gradle/api/internal/artifacts/ivyservice/ivyresolve/BuildableModuleVersionMetaDataResolveResult.java
new file mode 100644
index 0000000..c2ae2a1
--- /dev/null
+++ b/subprojects/core-impl/src/main/groovy/org/gradle/api/internal/artifacts/ivyservice/ivyresolve/BuildableModuleVersionMetaDataResolveResult.java
@@ -0,0 +1,83 @@
+/*
+ * Copyright 2012 the original author or authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY 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.ivyresolve;
+
+import org.apache.ivy.core.module.descriptor.ModuleDescriptor;
+import org.gradle.api.Nullable;
+import org.gradle.api.artifacts.ModuleVersionIdentifier;
+import org.gradle.api.internal.artifacts.ivyservice.ModuleVersionResolveException;
+
+/**
+ * The result of attempting to resolve a dependency descriptor to the meta-data for a module version.
+ */
+public interface BuildableModuleVersionMetaDataResolveResult {
+    static enum State {
+        Resolved, Missing, Failed, ProbablyMissing, Unknown
+    }
+
+    /**
+     * Returns the current state of this descriptor.
+     */
+    State getState();
+
+    /**
+     * Returns the meta-data.
+     *
+     * @throws ModuleVersionResolveException If the resolution was not successful.
+     */
+    ModuleVersionMetaData getMetaData() throws ModuleVersionResolveException;
+
+    @Nullable
+    ModuleVersionResolveException getFailure();
+
+    /**
+     * Marks the module version as resolved, with the given meta-data.
+     */
+    void resolved(ModuleDescriptor descriptor, boolean changing, ModuleSource moduleSource);
+
+    /**
+     * Marks the module version as resolved, with the given meta-data.
+     */
+    void resolved(ModuleVersionIdentifier id, ModuleDescriptor descriptor, boolean changing, ModuleSource moduleSource);
+
+    /**
+     * Marks the resolve as failed with the given exception.
+     */
+    void failed(ModuleVersionResolveException failure);
+
+    /**
+     * Marks the module version as definitely missing.
+     */
+    void missing();
+
+    /**
+     * Marks the module version as probably missing.
+     */
+    void probablyMissing();
+
+    /**
+     * The repository-specific source for this module version.
+     */
+    public ModuleSource getModuleSource();
+
+    void setModuleSource(ModuleSource moduleSource);
+
+    /**
+     * Replaces the dependencies of this module version.
+     */
+    void setDependencies(Iterable<? extends DependencyMetaData> dependencies);
+}
diff --git a/subprojects/core-impl/src/main/groovy/org/gradle/api/internal/artifacts/ivyservice/ivyresolve/CacheLockingModuleVersionRepository.java b/subprojects/core-impl/src/main/groovy/org/gradle/api/internal/artifacts/ivyservice/ivyresolve/CacheLockingModuleVersionRepository.java
index 514cced..d392459 100644
--- a/subprojects/core-impl/src/main/groovy/org/gradle/api/internal/artifacts/ivyservice/ivyresolve/CacheLockingModuleVersionRepository.java
+++ b/subprojects/core-impl/src/main/groovy/org/gradle/api/internal/artifacts/ivyservice/ivyresolve/CacheLockingModuleVersionRepository.java
@@ -15,7 +15,7 @@
  */
 package org.gradle.api.internal.artifacts.ivyservice.ivyresolve;
 
-import org.apache.ivy.core.module.descriptor.Artifact;
+import org.gradle.api.artifacts.ArtifactIdentifier;
 import org.gradle.api.internal.artifacts.ivyservice.BuildableArtifactResolveResult;
 import org.gradle.api.internal.artifacts.ivyservice.CacheLockingManager;
 
@@ -39,7 +39,7 @@ public class CacheLockingModuleVersionRepository implements ModuleVersionReposit
         return repository.getName();
     }
 
-    public void getDependency(final DependencyMetaData dependency, final BuildableModuleVersionMetaData result) {
+    public void getDependency(final DependencyMetaData dependency, final BuildableModuleVersionMetaDataResolveResult result) {
         cacheLockingManager.longRunningOperation(String.format("Resolve %s using repository %s", dependency, getId()), new Runnable() {
             public void run() {
                 repository.getDependency(dependency, result);
@@ -47,7 +47,7 @@ public class CacheLockingModuleVersionRepository implements ModuleVersionReposit
         });
     }
 
-    public void resolve(final Artifact artifact, final BuildableArtifactResolveResult result, final ModuleSource moduleSource) {
+    public void resolve(final ArtifactIdentifier artifact, final BuildableArtifactResolveResult result, final ModuleSource moduleSource) {
         cacheLockingManager.longRunningOperation(String.format("Download %s using repository %s", artifact, getId()), new Runnable() {
             public void run() {
                 repository.resolve(artifact, result, moduleSource);
diff --git a/subprojects/core-impl/src/main/groovy/org/gradle/api/internal/artifacts/ivyservice/ivyresolve/CachingModuleVersionRepository.java b/subprojects/core-impl/src/main/groovy/org/gradle/api/internal/artifacts/ivyservice/ivyresolve/CachingModuleVersionRepository.java
index 8b82cb5..37bccb6 100644
--- a/subprojects/core-impl/src/main/groovy/org/gradle/api/internal/artifacts/ivyservice/ivyresolve/CachingModuleVersionRepository.java
+++ b/subprojects/core-impl/src/main/groovy/org/gradle/api/internal/artifacts/ivyservice/ivyresolve/CachingModuleVersionRepository.java
@@ -15,16 +15,15 @@
  */
 package org.gradle.api.internal.artifacts.ivyservice.ivyresolve;
 
-import org.apache.ivy.core.module.descriptor.Artifact;
 import org.apache.ivy.core.module.id.ModuleRevisionId;
 import org.gradle.api.artifacts.ArtifactIdentifier;
 import org.gradle.api.artifacts.ModuleVersionIdentifier;
 import org.gradle.api.artifacts.ModuleVersionSelector;
-import org.gradle.api.internal.artifacts.DefaultArtifactIdentifier;
 import org.gradle.api.internal.artifacts.DefaultModuleVersionIdentifier;
 import org.gradle.api.internal.artifacts.DefaultModuleVersionSelector;
 import org.gradle.api.internal.artifacts.configurations.dynamicversion.CachePolicy;
 import org.gradle.api.internal.artifacts.ivyservice.BuildableArtifactResolveResult;
+import org.gradle.api.internal.artifacts.ivyservice.DependencyToModuleVersionResolver;
 import org.gradle.api.internal.artifacts.ivyservice.dynamicversions.ModuleResolutionCache;
 import org.gradle.api.internal.artifacts.ivyservice.modulecache.ModuleDescriptorCache;
 import org.gradle.api.internal.externalresource.cached.CachedArtifact;
@@ -37,12 +36,15 @@ import org.slf4j.LoggerFactory;
 import java.io.File;
 import java.math.BigInteger;
 
+import static org.gradle.api.internal.artifacts.DefaultModuleVersionIdentifier.newId;
+
 public class CachingModuleVersionRepository implements LocalAwareModuleVersionRepository {
     private static final Logger LOGGER = LoggerFactory.getLogger(CachingModuleVersionRepository.class);
 
     private final ModuleResolutionCache moduleResolutionCache;
     private final ModuleDescriptorCache moduleDescriptorCache;
     private final CachedArtifactIndex artifactAtRepositoryCachedResolutionIndex;
+    private final DependencyToModuleVersionResolver resolver;
 
     private final CachePolicy cachePolicy;
 
@@ -51,11 +53,13 @@ public class CachingModuleVersionRepository implements LocalAwareModuleVersionRe
 
     public CachingModuleVersionRepository(ModuleVersionRepository delegate, ModuleResolutionCache moduleResolutionCache, ModuleDescriptorCache moduleDescriptorCache,
                                           CachedArtifactIndex artifactAtRepositoryCachedResolutionIndex,
+                                          DependencyToModuleVersionResolver resolver,
                                           CachePolicy cachePolicy, TimeProvider timeProvider) {
         this.delegate = delegate;
         this.moduleDescriptorCache = moduleDescriptorCache;
         this.moduleResolutionCache = moduleResolutionCache;
         this.artifactAtRepositoryCachedResolutionIndex = artifactAtRepositoryCachedResolutionIndex;
+        this.resolver = resolver;
         this.timeProvider = timeProvider;
         this.cachePolicy = cachePolicy;
     }
@@ -73,24 +77,25 @@ public class CachingModuleVersionRepository implements LocalAwareModuleVersionRe
         return "Caching " + delegate.toString();
     }
 
-    public void getLocalDependency(DependencyMetaData dependency, BuildableModuleVersionMetaData result) {
+    public void getLocalDependency(DependencyMetaData dependency, BuildableModuleVersionMetaDataResolveResult result) {
         DependencyMetaData resolvedDependency = maybeUseCachedDynamicVersion(delegate, dependency);
         lookupModuleInCache(delegate, resolvedDependency, result);
     }
 
-    public void getDependency(DependencyMetaData dependency, BuildableModuleVersionMetaData result) {
+    public void getDependency(DependencyMetaData dependency, BuildableModuleVersionMetaDataResolveResult result) {
         DependencyMetaData forced = dependency.withChanging();
         delegate.getDependency(forced, result);
         switch (result.getState()) {
             case Missing:
                 final ModuleRevisionId dependencyRevisionId = dependency.getDescriptor().getDependencyRevisionId();
-                final DefaultModuleVersionIdentifier moduleVersionIdentifier = new DefaultModuleVersionIdentifier(dependencyRevisionId.getOrganisation(), dependencyRevisionId.getName(), dependencyRevisionId.getRevision());
+                final ModuleVersionIdentifier moduleVersionIdentifier = DefaultModuleVersionIdentifier.newId(dependencyRevisionId);
                 moduleDescriptorCache.cacheModuleDescriptor(delegate, moduleVersionIdentifier, null, null, dependency.isChanging());
                 break;
             case Resolved:
-                moduleResolutionCache.cacheModuleResolution(delegate, dependency.getDescriptor().getDependencyRevisionId(), result.getId());
+                ModuleVersionMetaData metaData = result.getMetaData();
+                moduleResolutionCache.cacheModuleResolution(delegate, dependency.getRequested(), metaData.getId());
                 final ModuleSource moduleSource = result.getModuleSource();
-                final ModuleDescriptorCache.CachedModuleDescriptor cachedModuleDescriptor = moduleDescriptorCache.cacheModuleDescriptor(delegate, result.getId(), result.getDescriptor(), moduleSource, isChangingDependency(dependency, result));
+                final ModuleDescriptorCache.CachedModuleDescriptor cachedModuleDescriptor = moduleDescriptorCache.cacheModuleDescriptor(delegate, metaData.getId(), metaData.getDescriptor(), moduleSource, isChangingDependency(dependency, metaData));
                 result.setModuleSource(new CachingModuleSource(cachedModuleDescriptor.getDescriptorHash(), cachedModuleDescriptor.isChangingModule(), moduleSource));
                 break;
             case Failed:
@@ -101,26 +106,25 @@ public class CachingModuleVersionRepository implements LocalAwareModuleVersionRe
     }
 
     private DependencyMetaData maybeUseCachedDynamicVersion(ModuleVersionRepository repository, DependencyMetaData original) {
-        ModuleRevisionId originalId = original.getDescriptor().getDependencyRevisionId();
-        ModuleResolutionCache.CachedModuleResolution cachedModuleResolution = moduleResolutionCache.getCachedModuleResolution(repository, originalId);
+        ModuleVersionSelector requested = original.getRequested();
+        ModuleResolutionCache.CachedModuleResolution cachedModuleResolution = moduleResolutionCache.getCachedModuleResolution(repository, requested);
         if (cachedModuleResolution != null && cachedModuleResolution.isDynamicVersion()) {
-            ModuleVersionSelector selector = createModuleVersionSelector(originalId);
             ModuleVersionIdentifier resolvedVersion = cachedModuleResolution.getResolvedVersion();
-            if (cachePolicy.mustRefreshDynamicVersion(selector, resolvedVersion, cachedModuleResolution.getAgeMillis())) {
-                LOGGER.debug("Resolved revision in dynamic revision cache is expired: will perform fresh resolve of '{}' in '{}'", selector, repository.getName());
+            if (cachePolicy.mustRefreshDynamicVersion(requested, resolvedVersion, cachedModuleResolution.getAgeMillis())) {
+                LOGGER.debug("Resolved revision in dynamic revision cache is expired: will perform fresh resolve of '{}' in '{}'", requested, repository.getName());
                 return original;
             } else {
-                LOGGER.debug("Found resolved revision in dynamic revision cache of '{}': Using '{}' for '{}'", repository.getName(), cachedModuleResolution.getResolvedVersion(), originalId);
+                LOGGER.debug("Found resolved revision in dynamic revision cache of '{}': Using '{}' for '{}'", repository.getName(), cachedModuleResolution.getResolvedVersion(), requested);
                 return original.withRequestedVersion(DefaultModuleVersionSelector.newSelector(resolvedVersion.getGroup(), resolvedVersion.getName(), resolvedVersion.getVersion()));
             }
         }
         return original;
     }
 
-    public void lookupModuleInCache(ModuleVersionRepository repository, DependencyMetaData dependency, BuildableModuleVersionMetaData result) {
+    public void lookupModuleInCache(ModuleVersionRepository repository, DependencyMetaData dependency, BuildableModuleVersionMetaDataResolveResult result) {
         ModuleRevisionId resolvedModuleVersionId = dependency.getDescriptor().getDependencyRevisionId();
-        ModuleVersionIdentifier moduleVersionIdentifier = createModuleVersionIdentifier(resolvedModuleVersionId);
-        ModuleDescriptorCache.CachedModuleDescriptor cachedModuleDescriptor = moduleDescriptorCache.getCachedModuleDescriptor(repository, moduleVersionIdentifier);
+        ModuleVersionIdentifier moduleVersionIdentifier = newId(resolvedModuleVersionId);
+        ModuleDescriptorCache.CachedModuleDescriptor cachedModuleDescriptor = moduleDescriptorCache.getCachedModuleDescriptor(repository, moduleVersionIdentifier, resolver);
         if (cachedModuleDescriptor == null) {
             return;
         }
@@ -163,26 +167,25 @@ public class CachingModuleVersionRepository implements LocalAwareModuleVersionRe
         return downloadedModule.isChanging();
     }
 
-    public void resolve(Artifact artifact, BuildableArtifactResolveResult result, ModuleSource moduleSource) {
-        ArtifactAtRepositoryKey resolutionCacheIndexKey = new ArtifactAtRepositoryKey(delegate, artifact.getId());
+    public void resolve(ArtifactIdentifier artifact, BuildableArtifactResolveResult result, ModuleSource moduleSource) {
+        ArtifactAtRepositoryKey resolutionCacheIndexKey = new ArtifactAtRepositoryKey(delegate.getId(), artifact);
         // Look in the cache for this resolver
         CachedArtifact cached = artifactAtRepositoryCachedResolutionIndex.lookup(resolutionCacheIndexKey);
         final CachingModuleSource cachedModuleSource = (CachingModuleSource) moduleSource;
         final BigInteger descriptorHash = cachedModuleSource.getDescriptorHash();
         if (cached != null) {
-            ArtifactIdentifier artifactIdentifier = createArtifactIdentifier(artifact);
             long age = timeProvider.getCurrentTime() - cached.getCachedAt();
             final boolean isChangingModule = cachedModuleSource.isChangingModule();
             if (cached.isMissing()) {
-                if (!cachePolicy.mustRefreshArtifact(artifactIdentifier, null, age, isChangingModule, descriptorHash.equals(cached.getDescriptorHash()))) {
-                    LOGGER.debug("Detected non-existence of artifact '{}' in resolver cache", artifact.getId());
+                if (!cachePolicy.mustRefreshArtifact(artifact, null, age, isChangingModule, descriptorHash.equals(cached.getDescriptorHash()))) {
+                    LOGGER.debug("Detected non-existence of artifact '{}' in resolver cache", artifact);
                     result.notFound(artifact);
                     return;
                 }
             } else {
                 File cachedArtifactFile = cached.getCachedFile();
-                if (!cachePolicy.mustRefreshArtifact(artifactIdentifier, cachedArtifactFile, age, isChangingModule, descriptorHash.equals(cached.getDescriptorHash()))) {
-                    LOGGER.debug("Found artifact '{}' in resolver cache: {}", artifact.getId(), cachedArtifactFile);
+                if (!cachePolicy.mustRefreshArtifact(artifact, cachedArtifactFile, age, isChangingModule, descriptorHash.equals(cached.getDescriptorHash()))) {
+                    LOGGER.debug("Found artifact '{}' in resolver cache: {}", artifact, cachedArtifactFile);
                     result.resolved(cachedArtifactFile);
                     return;
                 }
@@ -190,7 +193,7 @@ public class CachingModuleVersionRepository implements LocalAwareModuleVersionRe
         }
 
         delegate.resolve(artifact, result, cachedModuleSource.getDelegate());
-        LOGGER.debug("Downloaded artifact '{}' from resolver: {}", artifact.getId(), delegate);
+        LOGGER.debug("Downloaded artifact '{}' from resolver: {}", artifact, delegate.getName());
 
         if (result.getFailure() instanceof ArtifactNotFoundException) {
             artifactAtRepositoryCachedResolutionIndex.storeMissing(resolutionCacheIndexKey, descriptorHash);
@@ -199,19 +202,6 @@ public class CachingModuleVersionRepository implements LocalAwareModuleVersionRe
         }
     }
 
-    private ModuleVersionSelector createModuleVersionSelector(ModuleRevisionId moduleRevisionId) {
-        return new DefaultModuleVersionSelector(moduleRevisionId.getOrganisation(), moduleRevisionId.getName(), moduleRevisionId.getRevision());
-    }
-
-    private ModuleVersionIdentifier createModuleVersionIdentifier(ModuleRevisionId moduleRevisionId) {
-        return new DefaultModuleVersionIdentifier(moduleRevisionId.getOrganisation(), moduleRevisionId.getName(), moduleRevisionId.getRevision());
-    }
-
-    private ArtifactIdentifier createArtifactIdentifier(Artifact artifact) {
-        ModuleVersionIdentifier moduleVersionIdentifier = createModuleVersionIdentifier(artifact.getModuleRevisionId());
-        return new DefaultArtifactIdentifier(moduleVersionIdentifier, artifact.getName(), artifact.getType(), artifact.getExt(), artifact.getExtraAttribute("classifier"));
-    }
-
     static class CachingModuleSource implements ModuleSource {
         private final BigInteger descriptorHash;
         private final boolean changingModule;
diff --git a/subprojects/core-impl/src/main/groovy/org/gradle/api/internal/artifacts/ivyservice/ivyresolve/ConfigurationMetaData.java b/subprojects/core-impl/src/main/groovy/org/gradle/api/internal/artifacts/ivyservice/ivyresolve/ConfigurationMetaData.java
new file mode 100644
index 0000000..5e04a64
--- /dev/null
+++ b/subprojects/core-impl/src/main/groovy/org/gradle/api/internal/artifacts/ivyservice/ivyresolve/ConfigurationMetaData.java
@@ -0,0 +1,42 @@
+/*
+ * Copyright 2013 the original author or authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY 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.ivyresolve;
+
+import org.apache.ivy.core.module.descriptor.Artifact;
+import org.apache.ivy.core.module.descriptor.ExcludeRule;
+
+import java.util.List;
+import java.util.Set;
+
+public interface ConfigurationMetaData {
+    /**
+     * The set of configurations that this configuration extends. Includes this configuration.
+     */
+    Set<String> getHierarchy();
+
+    String getName();
+
+    ModuleVersionMetaData getModuleVersion();
+
+    List<DependencyMetaData> getDependencies();
+
+    Set<Artifact> getArtifacts();
+
+    Set<ExcludeRule> getExcludeRules();
+
+    boolean isTransitive();
+}
diff --git a/subprojects/core-impl/src/main/groovy/org/gradle/api/internal/artifacts/ivyservice/ivyresolve/ConfiguredModuleVersionRepository.java b/subprojects/core-impl/src/main/groovy/org/gradle/api/internal/artifacts/ivyservice/ivyresolve/ConfiguredModuleVersionRepository.java
new file mode 100644
index 0000000..57cdd62
--- /dev/null
+++ b/subprojects/core-impl/src/main/groovy/org/gradle/api/internal/artifacts/ivyservice/ivyresolve/ConfiguredModuleVersionRepository.java
@@ -0,0 +1,23 @@
+/*
+ * Copyright 2013 the original author or authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY 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.ivyresolve;
+
+public interface ConfiguredModuleVersionRepository extends ModuleVersionRepository {
+    boolean isDynamicResolveMode();
+
+    boolean isLocal();
+}
diff --git a/subprojects/core-impl/src/main/groovy/org/gradle/api/internal/artifacts/ivyservice/ivyresolve/DefaultBuildableModuleVersionMetaData.java b/subprojects/core-impl/src/main/groovy/org/gradle/api/internal/artifacts/ivyservice/ivyresolve/DefaultBuildableModuleVersionMetaData.java
deleted file mode 100644
index ec67ca1..0000000
--- a/subprojects/core-impl/src/main/groovy/org/gradle/api/internal/artifacts/ivyservice/ivyresolve/DefaultBuildableModuleVersionMetaData.java
+++ /dev/null
@@ -1,137 +0,0 @@
-/*
- * Copyright 2012 the original author or authors.
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- *      http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY 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.ivyresolve;
-
-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.gradle.api.artifacts.ModuleVersionIdentifier;
-import org.gradle.api.internal.artifacts.DefaultModuleVersionIdentifier;
-import org.gradle.api.internal.artifacts.ivyservice.ModuleVersionResolveException;
-import org.gradle.util.CollectionUtils;
-
-import java.util.ArrayList;
-import java.util.List;
-
-public class DefaultBuildableModuleVersionMetaData implements BuildableModuleVersionMetaData {
-    private ModuleDescriptor moduleDescriptor;
-    private boolean changing;
-    private State state = State.Unknown;
-    private ModuleSource moduleSource;
-    private List<DependencyMetaData> dependencies;
-    private ModuleVersionResolveException failure;
-    private ModuleVersionIdentifier moduleVersionIdentifier;
-
-    public void reset(State state) {
-        this.state = state;
-        moduleDescriptor = null;
-        changing = false;
-        failure = null;
-    }
-
-    public void resolved(ModuleDescriptor descriptor, boolean changing, ModuleSource moduleSource) {
-        ModuleRevisionId moduleRevisionId = descriptor.getModuleRevisionId();
-        DefaultModuleVersionIdentifier id = new DefaultModuleVersionIdentifier(moduleRevisionId.getOrganisation(), moduleRevisionId.getName(), moduleRevisionId.getRevision());
-        resolved(id, descriptor, changing, moduleSource);
-    }
-
-    public void resolved(ModuleVersionIdentifier id, ModuleDescriptor descriptor, boolean changing, ModuleSource moduleSource) {
-        reset(State.Resolved);
-        this.moduleVersionIdentifier = id;
-        this.moduleDescriptor = descriptor;
-        this.changing = changing;
-        this.moduleSource = moduleSource;
-    }
-
-    public void missing() {
-        reset(State.Missing);
-    }
-
-    public void probablyMissing() {
-        reset(State.ProbablyMissing);
-    }
-
-    public void failed(ModuleVersionResolveException failure) {
-        reset(State.Failed);
-        this.failure = failure;
-    }
-
-    public State getState() {
-        return state;
-    }
-
-    public ModuleVersionResolveException getFailure() {
-        assertHasResult();
-        return failure;
-    }
-
-    private void assertHasResult() {
-        if (state == State.Unknown) {
-            throw new IllegalStateException("No result has been specified.");
-        }
-    }
-
-    private void assertResolved() {
-        if (state == State.Failed) {
-            throw failure;
-        }
-        if (state != State.Resolved) {
-            throw new IllegalStateException("This module has not been resolved.");
-        }
-    }
-
-    public ModuleVersionIdentifier getId() {
-        assertResolved();
-        return moduleVersionIdentifier;
-    }
-
-    public ModuleDescriptor getDescriptor() {
-        assertResolved();
-        return moduleDescriptor;
-    }
-
-    public List<DependencyMetaData> getDependencies() {
-        assertResolved();
-        if (dependencies == null) {
-            dependencies = new ArrayList<DependencyMetaData>();
-            for (final DependencyDescriptor dependencyDescriptor : moduleDescriptor.getDependencies()) {
-                dependencies.add(new DefaultDependencyMetaData(dependencyDescriptor));
-            }
-        }
-        return dependencies;
-    }
-
-    public void setDependencies(Iterable<? extends DependencyMetaData> dependencies) {
-        assertResolved();
-        this.dependencies = CollectionUtils.toList(dependencies);
-    }
-
-    public boolean isChanging() {
-        assertResolved();
-        return changing;
-    }
-
-    public ModuleSource getModuleSource() {
-        assertResolved();
-        return moduleSource;
-    }
-
-    public void setModuleSource(ModuleSource moduleSource) {
-        assertResolved();
-        this.moduleSource = moduleSource;
-    }
-
-}
diff --git a/subprojects/core-impl/src/main/groovy/org/gradle/api/internal/artifacts/ivyservice/ivyresolve/DefaultBuildableModuleVersionMetaDataResolveResult.java b/subprojects/core-impl/src/main/groovy/org/gradle/api/internal/artifacts/ivyservice/ivyresolve/DefaultBuildableModuleVersionMetaDataResolveResult.java
new file mode 100644
index 0000000..61c8a01
--- /dev/null
+++ b/subprojects/core-impl/src/main/groovy/org/gradle/api/internal/artifacts/ivyservice/ivyresolve/DefaultBuildableModuleVersionMetaDataResolveResult.java
@@ -0,0 +1,286 @@
+/*
+ * Copyright 2012 the original author or authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY 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.ivyresolve;
+
+import org.apache.ivy.core.module.descriptor.*;
+import org.apache.ivy.core.module.id.ModuleRevisionId;
+import org.gradle.api.artifacts.ModuleVersionIdentifier;
+import org.gradle.api.internal.artifacts.DefaultModuleVersionIdentifier;
+import org.gradle.api.internal.artifacts.ivyservice.ModuleVersionResolveException;
+import org.gradle.util.CollectionUtils;
+
+import java.util.*;
+
+// TODO - split out a buildable ModuleVersionMetaData implementation
+public class DefaultBuildableModuleVersionMetaDataResolveResult implements BuildableModuleVersionMetaDataResolveResult, MutableModuleVersionMetaData {
+    private static final List<String> DEFAULT_STATUS_SCHEME = Arrays.asList("integration", "milestone", "release");
+
+    private ModuleDescriptor moduleDescriptor;
+    private boolean changing;
+    private State state = State.Unknown;
+    private ModuleSource moduleSource;
+    private List<DependencyMetaData> dependencies;
+    private Map<String, DefaultConfigurationMetaData> configurations = new HashMap<String, DefaultConfigurationMetaData>();
+    private ModuleVersionResolveException failure;
+    private ModuleVersionIdentifier moduleVersionIdentifier;
+    private String status;
+    private List<String> statusScheme = DEFAULT_STATUS_SCHEME;
+
+    public void reset(State state) {
+        this.state = state;
+        moduleDescriptor = null;
+        changing = false;
+        failure = null;
+        status = null;
+        statusScheme = DEFAULT_STATUS_SCHEME;
+    }
+
+    public void resolved(ModuleDescriptor descriptor, boolean changing, ModuleSource moduleSource) {
+        ModuleRevisionId moduleRevisionId = descriptor.getModuleRevisionId();
+        ModuleVersionIdentifier id = DefaultModuleVersionIdentifier.newId(moduleRevisionId);
+        resolved(id, descriptor, changing, moduleSource);
+    }
+
+    public void resolved(ModuleVersionIdentifier id, ModuleDescriptor descriptor, boolean changing, ModuleSource moduleSource) {
+        reset(State.Resolved);
+        this.moduleVersionIdentifier = id;
+        this.moduleDescriptor = descriptor;
+        this.changing = changing;
+        this.moduleSource = moduleSource;
+        this.status = moduleDescriptor.getStatus();
+    }
+
+    public void missing() {
+        reset(State.Missing);
+    }
+
+    public void probablyMissing() {
+        reset(State.ProbablyMissing);
+    }
+
+    public void failed(ModuleVersionResolveException failure) {
+        reset(State.Failed);
+        this.failure = failure;
+    }
+
+    public State getState() {
+        return state;
+    }
+
+    public ModuleVersionResolveException getFailure() {
+        assertHasResult();
+        return failure;
+    }
+
+    public MutableModuleVersionMetaData getMetaData() throws ModuleVersionResolveException {
+        assertResolved();
+        return this;
+    }
+
+    private void assertHasResult() {
+        if (state == State.Unknown) {
+            throw new IllegalStateException("No result has been specified.");
+        }
+    }
+
+    private void assertResolved() {
+        if (state == State.Failed) {
+            throw failure;
+        }
+        if (state != State.Resolved) {
+            throw new IllegalStateException("This module has not been resolved.");
+        }
+    }
+
+    public ModuleVersionIdentifier getId() {
+        assertResolved();
+        return moduleVersionIdentifier;
+    }
+
+    public ModuleDescriptor getDescriptor() {
+        assertResolved();
+        return moduleDescriptor;
+    }
+
+    public List<DependencyMetaData> getDependencies() {
+        assertResolved();
+        if (dependencies == null) {
+            dependencies = new ArrayList<DependencyMetaData>();
+            for (final DependencyDescriptor dependencyDescriptor : moduleDescriptor.getDependencies()) {
+                dependencies.add(new DefaultDependencyMetaData(dependencyDescriptor));
+            }
+        }
+        return dependencies;
+    }
+
+    public void setDependencies(Iterable<? extends DependencyMetaData> dependencies) {
+        assertResolved();
+        this.dependencies = CollectionUtils.toList(dependencies);
+        for (DefaultConfigurationMetaData configuration : configurations.values()) {
+            configuration.dependencies = null;
+        }
+    }
+
+    public DefaultConfigurationMetaData getConfiguration(final String name) {
+        assertResolved();
+        DefaultConfigurationMetaData configuration = configurations.get(name);
+        if (configuration == null) {
+            Configuration descriptor = moduleDescriptor.getConfiguration(name);
+            if (descriptor == null) {
+                return null;
+            }
+            Set<String> hierarchy = new LinkedHashSet<String>();
+            hierarchy.add(name);
+            for (String parent : descriptor.getExtends()) {
+                hierarchy.addAll(getConfiguration(parent).hierarchy);
+            }
+            configuration = new DefaultConfigurationMetaData(descriptor, hierarchy);
+            configurations.put(name, configuration);
+        }
+        return configuration;
+    }
+
+    public boolean isChanging() {
+        assertResolved();
+        return changing;
+    }
+
+    public ModuleSource getModuleSource() {
+        assertResolved();
+        return moduleSource;
+    }
+
+    public void setModuleSource(ModuleSource moduleSource) {
+        assertResolved();
+        this.moduleSource = moduleSource;
+    }
+
+    public String getStatus() {
+        assertResolved();
+        return status;
+    }
+
+    public List<String> getStatusScheme() {
+        assertResolved();
+        return statusScheme;
+    }
+
+    public void setChanging(boolean changing) {
+        assertResolved();
+        this.changing = changing;
+    }
+
+    public void setStatus(String status) {
+        assertResolved();
+        this.status = status;
+    }
+
+    public void setStatusScheme(List<String> statusScheme) {
+        assertResolved();
+        this.statusScheme = statusScheme;
+    }
+
+    private class DefaultConfigurationMetaData implements ConfigurationMetaData {
+        private final Configuration descriptor;
+        private final Set<String> hierarchy;
+        private List<DependencyMetaData> dependencies;
+        private Set<Artifact> artifacts;
+        private LinkedHashSet<ExcludeRule> excludeRules;
+
+        private DefaultConfigurationMetaData(Configuration descriptor, Set<String> hierarchy) {
+            this.descriptor = descriptor;
+            this.hierarchy = hierarchy;
+        }
+
+        public ModuleVersionMetaData getModuleVersion() {
+            return DefaultBuildableModuleVersionMetaDataResolveResult.this;
+        }
+
+        public String getName() {
+            return descriptor.getName();
+        }
+
+        public Set<String> getHierarchy() {
+            return hierarchy;
+        }
+
+        public boolean isTransitive() {
+            return descriptor.isTransitive();
+        }
+
+        public List<DependencyMetaData> getDependencies() {
+            if (dependencies == null) {
+                dependencies = new ArrayList<DependencyMetaData>();
+                for (DependencyMetaData dependency : DefaultBuildableModuleVersionMetaDataResolveResult.this.getDependencies()) {
+                    if (include(dependency)) {
+                        dependencies.add(dependency);
+                    }
+                }
+            }
+            return dependencies;
+        }
+
+        private boolean include(DependencyMetaData dependency) {
+            String[] moduleConfigurations = dependency.getDescriptor().getModuleConfigurations();
+            for (int i = 0; i < moduleConfigurations.length; i++) {
+                String moduleConfiguration = moduleConfigurations[i];
+                if (moduleConfiguration.equals("%") || hierarchy.contains(moduleConfiguration)) {
+                    return true;
+                }
+                if (moduleConfiguration.equals("*")) {
+                    boolean include = true;
+                    for (int j = i + 1; j < moduleConfigurations.length && moduleConfigurations[j].startsWith("!"); j++) {
+                        if (moduleConfigurations[j].substring(1).equals(getName())) {
+                            include = false;
+                            break;
+                        }
+                    }
+                    if (include) {
+                        return true;
+                    }
+                }
+            }
+            return false;
+        }
+
+        public Set<ExcludeRule> getExcludeRules() {
+            if (excludeRules == null) {
+                excludeRules = new LinkedHashSet<ExcludeRule>();
+                for (ExcludeRule excludeRule : moduleDescriptor.getAllExcludeRules()) {
+                    for (String config : excludeRule.getConfigurations()) {
+                        if (hierarchy.contains(config)) {
+                            excludeRules.add(excludeRule);
+                            break;
+                        }
+                    }
+                }
+            }
+            return excludeRules;
+        }
+
+        public Set<Artifact> getArtifacts() {
+            if (artifacts == null) {
+                artifacts = new LinkedHashSet<Artifact>();
+                for (String ancestor : hierarchy) {
+                    for (Artifact artifact : moduleDescriptor.getArtifacts(ancestor)) {
+                        artifacts.add(artifact);
+                    }
+                }
+            }
+            return artifacts;
+        }
+    }
+}
diff --git a/subprojects/core-impl/src/main/groovy/org/gradle/api/internal/artifacts/ivyservice/ivyresolve/DefaultDependencyMetaData.java b/subprojects/core-impl/src/main/groovy/org/gradle/api/internal/artifacts/ivyservice/ivyresolve/DefaultDependencyMetaData.java
index b442e75..9965fe3 100644
--- a/subprojects/core-impl/src/main/groovy/org/gradle/api/internal/artifacts/ivyservice/ivyresolve/DefaultDependencyMetaData.java
+++ b/subprojects/core-impl/src/main/groovy/org/gradle/api/internal/artifacts/ivyservice/ivyresolve/DefaultDependencyMetaData.java
@@ -26,9 +26,9 @@ import org.gradle.internal.UncheckedException;
 
 import java.lang.reflect.Field;
 
-class DefaultDependencyMetaData implements DependencyMetaData {
+public class DefaultDependencyMetaData implements DependencyMetaData {
     private final DependencyDescriptor dependencyDescriptor;
-    private DefaultModuleVersionSelector requested;
+    private final DefaultModuleVersionSelector requested;
 
     public DefaultDependencyMetaData(DependencyDescriptor dependencyDescriptor) {
         this.dependencyDescriptor = dependencyDescriptor;
@@ -49,6 +49,10 @@ class DefaultDependencyMetaData implements DependencyMetaData {
         return dependencyDescriptor.isChanging();
     }
 
+    public boolean isTransitive() {
+        return dependencyDescriptor.isTransitive();
+    }
+
     public DependencyDescriptor getDescriptor() {
         return dependencyDescriptor;
     }
diff --git a/subprojects/core-impl/src/main/groovy/org/gradle/api/internal/artifacts/ivyservice/ivyresolve/DefaultIvyAdapter.java b/subprojects/core-impl/src/main/groovy/org/gradle/api/internal/artifacts/ivyservice/ivyresolve/DefaultIvyAdapter.java
index 41e97ce..52cb316 100644
--- a/subprojects/core-impl/src/main/groovy/org/gradle/api/internal/artifacts/ivyservice/ivyresolve/DefaultIvyAdapter.java
+++ b/subprojects/core-impl/src/main/groovy/org/gradle/api/internal/artifacts/ivyservice/ivyresolve/DefaultIvyAdapter.java
@@ -15,23 +15,30 @@
  */
 package org.gradle.api.internal.artifacts.ivyservice.ivyresolve;
 
-import org.apache.ivy.core.resolve.ResolveData;
-import org.gradle.api.internal.artifacts.ivyservice.DependencyToModuleResolver;
+import org.gradle.api.internal.artifacts.ivyservice.DependencyToModuleVersionResolver;
+import org.gradle.api.internal.artifacts.ivyservice.ivyresolve.strategy.LatestStrategy;
+import org.gradle.api.internal.artifacts.ivyservice.ivyresolve.strategy.VersionMatcher;
 
 class DefaultIvyAdapter implements IvyAdapter {
-    private final ResolveData resolveData;
-    private final DependencyToModuleResolver userResolver;
+    private final VersionMatcher versionMatcher;
+    private final LatestStrategy latestStrategy;
+    private final DependencyToModuleVersionResolver userResolver;
 
-    public DefaultIvyAdapter(ResolveData resolveData, DependencyToModuleResolver userResolverChain) {
-        this.resolveData = resolveData;
+    public DefaultIvyAdapter(VersionMatcher versionMatcher, LatestStrategy latestStrategy, DependencyToModuleVersionResolver userResolverChain) {
+        this.versionMatcher = versionMatcher;
+        this.latestStrategy = latestStrategy;
         userResolver = userResolverChain;
     }
 
-    public ResolveData getResolveData() {
-        return resolveData;
+    public LatestStrategy getLatestStrategy() {
+        return latestStrategy;
     }
 
-    public DependencyToModuleResolver getDependencyToModuleResolver() {
+    public VersionMatcher getVersionMatcher() {
+        return versionMatcher;
+    }
+
+    public DependencyToModuleVersionResolver getDependencyToModuleResolver() {
         return userResolver;
     }
 }
diff --git a/subprojects/core-impl/src/main/groovy/org/gradle/api/internal/artifacts/ivyservice/ivyresolve/DelegatingDependencyResolver.java b/subprojects/core-impl/src/main/groovy/org/gradle/api/internal/artifacts/ivyservice/ivyresolve/DelegatingDependencyResolver.java
deleted file mode 100644
index c40b50a..0000000
--- a/subprojects/core-impl/src/main/groovy/org/gradle/api/internal/artifacts/ivyservice/ivyresolve/DelegatingDependencyResolver.java
+++ /dev/null
@@ -1,149 +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.api.internal.artifacts.ivyservice.ivyresolve;
-
-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.DependencyResolver;
-import org.apache.ivy.plugins.resolver.ResolverSettings;
-import org.apache.ivy.plugins.resolver.util.ResolvedResource;
-
-import java.io.File;
-import java.io.IOException;
-import java.text.ParseException;
-import java.util.Map;
-
-abstract class DelegatingDependencyResolver implements DependencyResolver {
-    private final DependencyResolver resolver;
-    private ResolverSettings settings;
-
-    public DelegatingDependencyResolver(DependencyResolver resolver) {
-        this.resolver = resolver;
-    }
-
-    public DependencyResolver getResolver() {
-        return resolver;
-    }
-
-    public String getName() {
-        return resolver.getName();
-    }
-
-    public ResolverSettings getSettings() {
-        return settings;
-    }
-
-    public void setSettings(ResolverSettings settings) {
-        this.settings = settings;
-        resolver.setSettings(settings);
-    }
-
-    public Namespace getNamespace() {
-        return resolver.getNamespace();
-    }
-
-    public RepositoryCacheManager getRepositoryCacheManager() {
-        return resolver.getRepositoryCacheManager();
-    }
-
-    public ModuleEntry[] listModules(OrganisationEntry org) {
-        return resolver.listModules(org);
-    }
-
-    public OrganisationEntry[] listOrganisations() {
-        return resolver.listOrganisations();
-    }
-
-    public RevisionEntry[] listRevisions(ModuleEntry module) {
-        return resolver.listRevisions(module);
-    }
-
-    public String[] listTokenValues(String token, Map otherTokenValues) {
-        return resolver.listTokenValues(token, otherTokenValues);
-    }
-
-    public Map[] listTokenValues(String[] tokens, Map criteria) {
-        return resolver.listTokenValues(tokens, criteria);
-    }
-
-    public ArtifactOrigin locate(Artifact artifact) {
-        return resolver.locate(artifact);
-    }
-
-    public ResolvedModuleRevision getDependency(DependencyDescriptor dd, ResolveData data) throws ParseException {
-        return resolver.getDependency(dd, data);
-    }
-
-    public ResolvedResource findIvyFileRef(DependencyDescriptor dd, ResolveData data) {
-        return resolver.findIvyFileRef(dd, data);
-    }
-
-    public boolean exists(Artifact artifact) {
-        return resolver.exists(artifact);
-    }
-
-    public DownloadReport download(Artifact[] artifacts, DownloadOptions options) {
-        return resolver.download(artifacts, options);
-    }
-
-    public ArtifactDownloadReport download(ArtifactOrigin artifact, DownloadOptions options) {
-        return resolver.download(artifact, options);
-    }
-
-    public void abortPublishTransaction() throws IOException {
-        throw new UnsupportedOperationException();
-    }
-
-    public void setName(String name) {
-        throw new UnsupportedOperationException();
-    }
-
-    public void publish(Artifact artifact, File src, boolean overwrite) throws IOException {
-        throw new UnsupportedOperationException();
-    }
-
-    public void beginPublishTransaction(ModuleRevisionId module, boolean overwrite) throws IOException {
-        throw new UnsupportedOperationException();
-    }
-
-    public void commitPublishTransaction() throws IOException {
-        throw new UnsupportedOperationException();
-    }
-
-    public void reportFailure() {
-        throw new UnsupportedOperationException();
-    }
-
-    public void reportFailure(Artifact art) {
-        throw new UnsupportedOperationException();
-    }
-
-    public void dumpSettings() {
-        throw new UnsupportedOperationException();
-    }
-}
diff --git a/subprojects/core-impl/src/main/groovy/org/gradle/api/internal/artifacts/ivyservice/ivyresolve/DependencyMetaData.java b/subprojects/core-impl/src/main/groovy/org/gradle/api/internal/artifacts/ivyservice/ivyresolve/DependencyMetaData.java
index 889998e..fb3eaeb 100644
--- a/subprojects/core-impl/src/main/groovy/org/gradle/api/internal/artifacts/ivyservice/ivyresolve/DependencyMetaData.java
+++ b/subprojects/core-impl/src/main/groovy/org/gradle/api/internal/artifacts/ivyservice/ivyresolve/DependencyMetaData.java
@@ -26,6 +26,8 @@ public interface DependencyMetaData {
 
     boolean isChanging();
 
+    boolean isTransitive();
+
     /**
      * Returns a copy of this dependency with the given requested version.
      */
diff --git a/subprojects/core-impl/src/main/groovy/org/gradle/api/internal/artifacts/ivyservice/ivyresolve/DependencyResolverIdentifier.java b/subprojects/core-impl/src/main/groovy/org/gradle/api/internal/artifacts/ivyservice/ivyresolve/DependencyResolverIdentifier.java
index d7e03a4..1950cfc 100644
--- a/subprojects/core-impl/src/main/groovy/org/gradle/api/internal/artifacts/ivyservice/ivyresolve/DependencyResolverIdentifier.java
+++ b/subprojects/core-impl/src/main/groovy/org/gradle/api/internal/artifacts/ivyservice/ivyresolve/DependencyResolverIdentifier.java
@@ -25,22 +25,10 @@ import java.util.ArrayList;
 import java.util.List;
 
 public class DependencyResolverIdentifier {
-    private final String resolverId;
-    private final String resolverName;
-
-    public DependencyResolverIdentifier(DependencyResolver resolver) {
-        resolverName = resolver.getName();
-
+    public static String forIvyResolver(DependencyResolver resolver) {
         List<String> parts = new ArrayList<String>();
         parts.add(resolver.getClass().getName());
-        if (resolver instanceof ExternalResourceResolver) {
-            ExternalResourceResolver externalResourceResolver = (ExternalResourceResolver) resolver;
-            parts.add(joinPatterns(externalResourceResolver.getIvyPatterns()));
-            parts.add(joinPatterns(externalResourceResolver.getArtifactPatterns()));
-            if (externalResourceResolver.isM2compatible()) {
-                parts.add("m2compatible");
-            }
-        } else if (resolver instanceof AbstractPatternsBasedResolver) {
+        if (resolver instanceof AbstractPatternsBasedResolver) {
             AbstractPatternsBasedResolver patternsBasedResolver = (AbstractPatternsBasedResolver) resolver;
             parts.add(joinPatterns(patternsBasedResolver.getIvyPatterns()));
             parts.add(joinPatterns(patternsBasedResolver.getArtifactPatterns()));
@@ -52,23 +40,27 @@ public class DependencyResolverIdentifier {
             // TODO We should not be assuming equality between resolvers here based on name...
         }
 
-        resolverId = calculateId(parts);
+        return calculateId(parts);
     }
 
-    private String joinPatterns(List<String> patterns) {
+    // TODO: Move this logic into ExternalResourceResolver, and add some transport-specific information (bumping the cache version)
+    public static String forExternalResourceResolver(ExternalResourceResolver resolver) {
+        List<String> parts = new ArrayList<String>();
+        parts.add(resolver.getClass().getName());
+        parts.add(joinPatterns(resolver.getIvyPatterns()));
+        parts.add(joinPatterns(resolver.getArtifactPatterns()));
+        if (resolver.isM2compatible()) {
+            parts.add("m2compatible");
+        }
+        return calculateId(parts);
+    }
+
+    private static String joinPatterns(List<String> patterns) {
         return CollectionUtils.join(",", patterns);
     }
 
-    private String calculateId(List<String> parts) {
+    private static String calculateId(List<String> parts) {
         String idString = CollectionUtils.join("::", parts);
         return HashUtil.createHash(idString, "MD5").asHexString();
     }
-
-    public String getUniqueId() {
-        return resolverId;
-    }
-
-    public String getName() {
-        return resolverName;
-    }
 }
diff --git a/subprojects/core-impl/src/main/groovy/org/gradle/api/internal/artifacts/ivyservice/ivyresolve/ExternalResourceResolverAdapter.java b/subprojects/core-impl/src/main/groovy/org/gradle/api/internal/artifacts/ivyservice/ivyresolve/ExternalResourceResolverAdapter.java
deleted file mode 100644
index e634fa2..0000000
--- a/subprojects/core-impl/src/main/groovy/org/gradle/api/internal/artifacts/ivyservice/ivyresolve/ExternalResourceResolverAdapter.java
+++ /dev/null
@@ -1,47 +0,0 @@
-/*
- * Copyright 2012 the original author or authors.
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- *      http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY 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.ivyresolve;
-
-import org.apache.ivy.core.module.descriptor.Artifact;
-import org.gradle.api.internal.artifacts.ivyservice.BuildableArtifactResolveResult;
-import org.gradle.api.internal.artifacts.repositories.resolver.ExternalResourceResolver;
-
-/**
- * A {@link ModuleVersionRepository} wrapper around an {@link ExternalResourceResolver}.
- */
-public class ExternalResourceResolverAdapter extends AbstractDependencyResolverAdapter {
-    private final ExternalResourceResolver resolver;
-    private final boolean dynamicResolve;
-
-    public ExternalResourceResolverAdapter(ExternalResourceResolver resolver, boolean dynamicResolve) {
-        super(resolver);
-        this.resolver = resolver;
-        this.dynamicResolve = dynamicResolve;
-    }
-
-    @Override
-    public boolean isDynamicResolveMode() {
-        return dynamicResolve;
-    }
-
-    public void getDependency(DependencyMetaData dependency, BuildableModuleVersionMetaData result) {
-        resolver.getDependency(dependency.getDescriptor(), result);
-    }
-
-    public void resolve(Artifact artifact, BuildableArtifactResolveResult result, ModuleSource moduleSource) {
-        resolver.resolve(artifact, result, moduleSource);
-    }
-}
diff --git a/subprojects/core-impl/src/main/groovy/org/gradle/api/internal/artifacts/ivyservice/ivyresolve/IvyAdapter.java b/subprojects/core-impl/src/main/groovy/org/gradle/api/internal/artifacts/ivyservice/ivyresolve/IvyAdapter.java
index 824b41b..8cd985f 100644
--- a/subprojects/core-impl/src/main/groovy/org/gradle/api/internal/artifacts/ivyservice/ivyresolve/IvyAdapter.java
+++ b/subprojects/core-impl/src/main/groovy/org/gradle/api/internal/artifacts/ivyservice/ivyresolve/IvyAdapter.java
@@ -15,11 +15,14 @@
  */
 package org.gradle.api.internal.artifacts.ivyservice.ivyresolve;
 
-import org.apache.ivy.core.resolve.ResolveData;
-import org.gradle.api.internal.artifacts.ivyservice.DependencyToModuleResolver;
+import org.gradle.api.internal.artifacts.ivyservice.DependencyToModuleVersionResolver;
+import org.gradle.api.internal.artifacts.ivyservice.ivyresolve.strategy.LatestStrategy;
+import org.gradle.api.internal.artifacts.ivyservice.ivyresolve.strategy.VersionMatcher;
 
 public interface IvyAdapter {
-    ResolveData getResolveData();
+    VersionMatcher getVersionMatcher();
 
-    DependencyToModuleResolver getDependencyToModuleResolver();
+    LatestStrategy getLatestStrategy();
+
+    DependencyToModuleVersionResolver getDependencyToModuleResolver();
 }
diff --git a/subprojects/core-impl/src/main/groovy/org/gradle/api/internal/artifacts/ivyservice/ivyresolve/IvyAwareModuleVersionRepository.java b/subprojects/core-impl/src/main/groovy/org/gradle/api/internal/artifacts/ivyservice/ivyresolve/IvyAwareModuleVersionRepository.java
index b999020..1b66ddc 100644
--- a/subprojects/core-impl/src/main/groovy/org/gradle/api/internal/artifacts/ivyservice/ivyresolve/IvyAwareModuleVersionRepository.java
+++ b/subprojects/core-impl/src/main/groovy/org/gradle/api/internal/artifacts/ivyservice/ivyresolve/IvyAwareModuleVersionRepository.java
@@ -16,13 +16,11 @@
 
 package org.gradle.api.internal.artifacts.ivyservice.ivyresolve;
 
+import org.apache.ivy.core.resolve.ResolveData;
 import org.apache.ivy.core.settings.IvySettings;
 
 public interface IvyAwareModuleVersionRepository extends ModuleVersionRepository {
     void setSettings(IvySettings settings);
 
-    // TODO - move this
-    boolean isDynamicResolveMode();
-
-    boolean isLocal();
+    void setResolveData(ResolveData resolveData);
 }
diff --git a/subprojects/core-impl/src/main/groovy/org/gradle/api/internal/artifacts/ivyservice/ivyresolve/IvyContextualiser.java b/subprojects/core-impl/src/main/groovy/org/gradle/api/internal/artifacts/ivyservice/ivyresolve/IvyContextualiser.java
index b92dafb..f4c7a63 100755
--- a/subprojects/core-impl/src/main/groovy/org/gradle/api/internal/artifacts/ivyservice/ivyresolve/IvyContextualiser.java
+++ b/subprojects/core-impl/src/main/groovy/org/gradle/api/internal/artifacts/ivyservice/ivyresolve/IvyContextualiser.java
@@ -15,46 +15,12 @@
  */
 package org.gradle.api.internal.artifacts.ivyservice.ivyresolve;
 
-import org.apache.ivy.Ivy;
 import org.apache.ivy.core.IvyContext;
-import org.apache.ivy.core.resolve.ResolveData;
-import org.gradle.internal.UncheckedException;
-
-import java.lang.reflect.InvocationHandler;
-import java.lang.reflect.InvocationTargetException;
-import java.lang.reflect.Method;
-import java.lang.reflect.Proxy;
 
 public class IvyContextualiser {
-    private final Ivy ivy;
-    private final ResolveData resolveData;
-
-    public IvyContextualiser(Ivy ivy, ResolveData resolveData) {
-        this.ivy = ivy;
-        this.resolveData = resolveData;
-    }
-    
-    public <T> T contextualise(Class<T> type, final T delegate) {
-        Object proxy = Proxy.newProxyInstance(getClass().getClassLoader(), new Class[]{type}, new InvocationHandler() {
-            public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
-                IvyContext context = IvyContext.pushNewCopyContext();
-                try {
-                    context.setIvy(ivy);
-                    context.setResolveData(resolveData);
-                    return method.invoke(delegate, args);
-                } catch (InvocationTargetException e) {
-                    throw UncheckedException.throwAsUncheckedException(e.getTargetException());
-                } finally {
-                    IvyContext.popContext();
-                }
-            }
-        });
-        return type.cast(proxy);
-    }
-
     public static IvyContext getIvyContext() {
         IvyContext context = IvyContext.getContext();
-        if (context.peekIvy() == null || context.getResolveData() == null) {
+        if (context.peekIvy() == null) {
             throw new IllegalStateException("Ivy context not established");
         }
         return context;
diff --git a/subprojects/core-impl/src/main/groovy/org/gradle/api/internal/artifacts/ivyservice/ivyresolve/IvyDependencyResolverAdapter.java b/subprojects/core-impl/src/main/groovy/org/gradle/api/internal/artifacts/ivyservice/ivyresolve/IvyDependencyResolverAdapter.java
index e4fe31b..bf20aef 100644
--- a/subprojects/core-impl/src/main/groovy/org/gradle/api/internal/artifacts/ivyservice/ivyresolve/IvyDependencyResolverAdapter.java
+++ b/subprojects/core-impl/src/main/groovy/org/gradle/api/internal/artifacts/ivyservice/ivyresolve/IvyDependencyResolverAdapter.java
@@ -15,15 +15,20 @@
  */
 package org.gradle.api.internal.artifacts.ivyservice.ivyresolve;
 
+import org.apache.ivy.core.IvyContext;
 import org.apache.ivy.core.module.descriptor.Artifact;
 import org.apache.ivy.core.report.ArtifactDownloadReport;
 import org.apache.ivy.core.report.DownloadStatus;
 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.settings.IvySettings;
 import org.apache.ivy.plugins.resolver.DependencyResolver;
+import org.gradle.api.artifacts.ArtifactIdentifier;
+import org.gradle.api.internal.artifacts.DefaultArtifactIdentifier;
 import org.gradle.api.internal.artifacts.ivyservice.BuildableArtifactResolveResult;
-import org.gradle.api.internal.artifacts.repositories.cachemanager.EnhancedArtifactDownloadReport;
+import org.gradle.api.internal.artifacts.repositories.legacy.EnhancedArtifactDownloadReport;
+import org.gradle.api.internal.artifacts.repositories.legacy.LocalFileRepositoryCacheManager;
 import org.gradle.internal.UncheckedException;
 import org.slf4j.Logger;
 import org.slf4j.LoggerFactory;
@@ -34,16 +39,49 @@ import java.text.ParseException;
 /**
  * A {@link ModuleVersionRepository} wrapper around an Ivy {@link DependencyResolver}.
  */
-public class IvyDependencyResolverAdapter extends AbstractDependencyResolverAdapter {
+public class IvyDependencyResolverAdapter implements ConfiguredModuleVersionRepository, IvyAwareModuleVersionRepository {
     private static final Logger LOGGER = LoggerFactory.getLogger(IvyDependencyResolverAdapter.class);
     private final DownloadOptions downloadOptions = new DownloadOptions();
+    private final String identifier;
+    private final DependencyResolver resolver;
+    private ResolveData resolveData;
 
     public IvyDependencyResolverAdapter(DependencyResolver resolver) {
-        super(resolver);
+        this.resolver = resolver;
+        identifier = DependencyResolverIdentifier.forIvyResolver(resolver);
     }
 
-    public void getDependency(DependencyMetaData dependency, BuildableModuleVersionMetaData result) {
-        ResolveData resolveData = IvyContextualiser.getIvyContext().getResolveData();
+    public String getId() {
+        return identifier;
+    }
+
+    public String getName() {
+        return resolver.getName();
+    }
+
+    @Override
+    public String toString() {
+        return String.format("Repository '%s'", resolver.getName());
+    }
+
+    public boolean isLocal() {
+        return resolver.getRepositoryCacheManager() instanceof LocalFileRepositoryCacheManager;
+    }
+
+    public void setSettings(IvySettings settings) {
+        settings.addResolver(resolver);
+    }
+
+    public void setResolveData(ResolveData resolveData) {
+        this.resolveData = resolveData;
+    }
+
+    public boolean isDynamicResolveMode() {
+        return false;
+    }
+
+    public void getDependency(DependencyMetaData dependency, BuildableModuleVersionMetaDataResolveResult result) {
+        IvyContext.getContext().setResolveData(resolveData);
         try {
             ResolvedModuleRevision revision = resolver.getDependency(dependency.getDescriptor(), resolveData);
             if (revision == null) {
@@ -58,14 +96,15 @@ public class IvyDependencyResolverAdapter extends AbstractDependencyResolverAdap
         }
     }
 
-    public void resolve(Artifact artifact, BuildableArtifactResolveResult result, ModuleSource moduleSource) {
-        ArtifactDownloadReport artifactDownloadReport = resolver.download(new Artifact[]{artifact}, downloadOptions).getArtifactReport(artifact);
+    public void resolve(ArtifactIdentifier artifact, BuildableArtifactResolveResult result, ModuleSource moduleSource) {
+        Artifact ivyArtifact = DefaultArtifactIdentifier.toArtifact(artifact);
+        ArtifactDownloadReport artifactDownloadReport = resolver.download(new Artifact[]{ivyArtifact}, downloadOptions).getArtifactReport(ivyArtifact);
         if (downloadFailed(artifactDownloadReport)) {
             if (artifactDownloadReport instanceof EnhancedArtifactDownloadReport) {
                 EnhancedArtifactDownloadReport enhancedReport = (EnhancedArtifactDownloadReport) artifactDownloadReport;
-                result.failed(new ArtifactResolveException(artifactDownloadReport.getArtifact(), enhancedReport.getFailure()));
+                result.failed(new ArtifactResolveException(artifact, enhancedReport.getFailure()));
             } else {
-                result.failed(new ArtifactResolveException(artifactDownloadReport.getArtifact(), artifactDownloadReport.getDownloadDetails()));
+                result.failed(new ArtifactResolveException(artifact, artifactDownloadReport.getDownloadDetails()));
             }
             return;
         }
diff --git a/subprojects/core-impl/src/main/groovy/org/gradle/api/internal/artifacts/ivyservice/ivyresolve/IvyDynamicResolveModuleVersionRepository.java b/subprojects/core-impl/src/main/groovy/org/gradle/api/internal/artifacts/ivyservice/ivyresolve/IvyDynamicResolveModuleVersionRepository.java
index 821e0d8..d061381 100644
--- a/subprojects/core-impl/src/main/groovy/org/gradle/api/internal/artifacts/ivyservice/ivyresolve/IvyDynamicResolveModuleVersionRepository.java
+++ b/subprojects/core-impl/src/main/groovy/org/gradle/api/internal/artifacts/ivyservice/ivyresolve/IvyDynamicResolveModuleVersionRepository.java
@@ -16,7 +16,7 @@
 
 package org.gradle.api.internal.artifacts.ivyservice.ivyresolve;
 
-import org.apache.ivy.core.module.descriptor.Artifact;
+import org.gradle.api.artifacts.ArtifactIdentifier;
 import org.gradle.api.internal.artifacts.ivyservice.BuildableArtifactResolveResult;
 
 import java.util.ArrayList;
@@ -37,29 +37,29 @@ public class IvyDynamicResolveModuleVersionRepository implements LocalAwareModul
         return repository.getName();
     }
 
-    public void getLocalDependency(DependencyMetaData dependency, BuildableModuleVersionMetaData result) {
+    public void getLocalDependency(DependencyMetaData dependency, BuildableModuleVersionMetaDataResolveResult result) {
         repository.getLocalDependency(dependency, result);
-        if (result.getState() == BuildableModuleVersionMetaData.State.Resolved) {
+        if (result.getState() == BuildableModuleVersionMetaDataResolveResult.State.Resolved) {
             transformDependencies(result);
         }
     }
 
-    public void getDependency(DependencyMetaData dependency, BuildableModuleVersionMetaData result) {
+    public void getDependency(DependencyMetaData dependency, BuildableModuleVersionMetaDataResolveResult result) {
         repository.getDependency(dependency, result);
-        if (result.getState() == BuildableModuleVersionMetaData.State.Resolved) {
+        if (result.getState() == BuildableModuleVersionMetaDataResolveResult.State.Resolved) {
             transformDependencies(result);
         }
     }
 
-    private void transformDependencies(BuildableModuleVersionMetaData result) {
+    private void transformDependencies(BuildableModuleVersionMetaDataResolveResult result) {
         List<DependencyMetaData> transformed = new ArrayList<DependencyMetaData>();
-        for (DependencyMetaData dependency : result.getDependencies()) {
+        for (DependencyMetaData dependency : result.getMetaData().getDependencies()) {
             transformed.add(dependency.withRequestedVersion(dependency.getDescriptor().getDynamicConstraintDependencyRevisionId().getRevision()));
         }
         result.setDependencies(transformed);
     }
 
-    public void resolve(Artifact artifact, BuildableArtifactResolveResult result, ModuleSource moduleSource) {
+    public void resolve(ArtifactIdentifier artifact, BuildableArtifactResolveResult result, ModuleSource moduleSource) {
         repository.resolve(artifact, result, moduleSource);
     }
 }
diff --git a/subprojects/core-impl/src/main/groovy/org/gradle/api/internal/artifacts/ivyservice/ivyresolve/LazyDependencyToModuleResolver.java b/subprojects/core-impl/src/main/groovy/org/gradle/api/internal/artifacts/ivyservice/ivyresolve/LazyDependencyToModuleResolver.java
index 637cb5b..5c53c8f 100755
--- a/subprojects/core-impl/src/main/groovy/org/gradle/api/internal/artifacts/ivyservice/ivyresolve/LazyDependencyToModuleResolver.java
+++ b/subprojects/core-impl/src/main/groovy/org/gradle/api/internal/artifacts/ivyservice/ivyresolve/LazyDependencyToModuleResolver.java
@@ -18,12 +18,12 @@ package org.gradle.api.internal.artifacts.ivyservice.ivyresolve;
 import org.apache.ivy.core.module.descriptor.Artifact;
 import org.apache.ivy.core.module.descriptor.Configuration;
 import org.apache.ivy.core.module.descriptor.ModuleDescriptor;
-import org.apache.ivy.plugins.version.VersionMatcher;
 import org.gradle.api.artifacts.ModuleVersionIdentifier;
 import org.gradle.api.artifacts.ModuleVersionSelector;
 import org.gradle.api.artifacts.result.ModuleVersionSelectionReason;
 import org.gradle.api.internal.artifacts.DefaultModuleVersionIdentifier;
 import org.gradle.api.internal.artifacts.ivyservice.*;
+import org.gradle.api.internal.artifacts.ivyservice.ivyresolve.strategy.VersionMatcher;
 import org.gradle.api.internal.artifacts.ivyservice.resolveengine.result.VersionSelectionReasons;
 
 /**
@@ -31,16 +31,16 @@ import org.gradle.api.internal.artifacts.ivyservice.resolveengine.result.Version
  * required.
  */
 public class LazyDependencyToModuleResolver implements DependencyToModuleVersionIdResolver {
-    private final DependencyToModuleResolver dependencyResolver;
+    private final DependencyToModuleVersionResolver dependencyResolver;
     private final VersionMatcher versionMatcher;
 
-    public LazyDependencyToModuleResolver(DependencyToModuleResolver dependencyResolver, VersionMatcher versionMatcher) {
+    public LazyDependencyToModuleResolver(DependencyToModuleVersionResolver dependencyResolver, VersionMatcher versionMatcher) {
         this.dependencyResolver = dependencyResolver;
         this.versionMatcher = versionMatcher;
     }
 
     public ModuleVersionIdResolveResult resolve(DependencyMetaData dependency) {
-        if (versionMatcher.isDynamic(dependency.getDescriptor().getDependencyRevisionId())) {
+        if (versionMatcher.isDynamic(dependency.getRequested().getVersion())) {
             DynamicVersionResolveResult result = new DynamicVersionResolveResult(dependency);
             result.resolve();
             return result;
diff --git a/subprojects/core-impl/src/main/groovy/org/gradle/api/internal/artifacts/ivyservice/ivyresolve/LocalAwareModuleVersionRepository.java b/subprojects/core-impl/src/main/groovy/org/gradle/api/internal/artifacts/ivyservice/ivyresolve/LocalAwareModuleVersionRepository.java
index 2066cb0..b81524a 100644
--- a/subprojects/core-impl/src/main/groovy/org/gradle/api/internal/artifacts/ivyservice/ivyresolve/LocalAwareModuleVersionRepository.java
+++ b/subprojects/core-impl/src/main/groovy/org/gradle/api/internal/artifacts/ivyservice/ivyresolve/LocalAwareModuleVersionRepository.java
@@ -20,10 +20,10 @@ public interface LocalAwareModuleVersionRepository extends ModuleVersionReposito
     /**
      * Locates the given dependency, using only local resources.
      */
-    void getLocalDependency(DependencyMetaData dependency, BuildableModuleVersionMetaData result);
+    void getLocalDependency(DependencyMetaData dependency, BuildableModuleVersionMetaDataResolveResult result);
 
     /**
-     * Locates the given dependency, using whichever resources are appropriate. Always called after {@link #getLocalDependency(DependencyMetaData, BuildableModuleVersionMetaData)}.
+     * Locates the given dependency, using whichever resources are appropriate. Always called after {@link #getLocalDependency(DependencyMetaData, BuildableModuleVersionMetaDataResolveResult)}.
      */
-    void getDependency(DependencyMetaData dependency, BuildableModuleVersionMetaData result);
+    void getDependency(DependencyMetaData dependency, BuildableModuleVersionMetaDataResolveResult result);
 }
diff --git a/subprojects/core-impl/src/main/groovy/org/gradle/api/internal/artifacts/ivyservice/ivyresolve/LocalModuleVersionRepository.java b/subprojects/core-impl/src/main/groovy/org/gradle/api/internal/artifacts/ivyservice/ivyresolve/LocalModuleVersionRepository.java
index 93268b6..034d03d 100644
--- a/subprojects/core-impl/src/main/groovy/org/gradle/api/internal/artifacts/ivyservice/ivyresolve/LocalModuleVersionRepository.java
+++ b/subprojects/core-impl/src/main/groovy/org/gradle/api/internal/artifacts/ivyservice/ivyresolve/LocalModuleVersionRepository.java
@@ -16,7 +16,7 @@
 
 package org.gradle.api.internal.artifacts.ivyservice.ivyresolve;
 
-import org.apache.ivy.core.module.descriptor.Artifact;
+import org.gradle.api.artifacts.ArtifactIdentifier;
 import org.gradle.api.internal.artifacts.ivyservice.BuildableArtifactResolveResult;
 
 public class LocalModuleVersionRepository implements LocalAwareModuleVersionRepository {
@@ -26,15 +26,15 @@ public class LocalModuleVersionRepository implements LocalAwareModuleVersionRepo
         this.delegate = delegate;
     }
 
-    public void resolve(Artifact artifact, BuildableArtifactResolveResult result, ModuleSource moduleSource) {
+    public void resolve(ArtifactIdentifier artifact, BuildableArtifactResolveResult result, ModuleSource moduleSource) {
         delegate.resolve(artifact, result, moduleSource);
     }
 
-    public void getLocalDependency(DependencyMetaData dependency, BuildableModuleVersionMetaData result) {
+    public void getLocalDependency(DependencyMetaData dependency, BuildableModuleVersionMetaDataResolveResult result) {
         delegate.getDependency(dependency, result);
     }
 
-    public void getDependency(DependencyMetaData dependency, BuildableModuleVersionMetaData result) {
+    public void getDependency(DependencyMetaData dependency, BuildableModuleVersionMetaDataResolveResult result) {
         result.missing();
     }
 
diff --git a/subprojects/core-impl/src/main/groovy/org/gradle/api/internal/artifacts/ivyservice/ivyresolve/LoopbackDependencyResolver.java b/subprojects/core-impl/src/main/groovy/org/gradle/api/internal/artifacts/ivyservice/ivyresolve/LoopbackDependencyResolver.java
index 38f1308..2b5d3ec 100644
--- a/subprojects/core-impl/src/main/groovy/org/gradle/api/internal/artifacts/ivyservice/ivyresolve/LoopbackDependencyResolver.java
+++ b/subprojects/core-impl/src/main/groovy/org/gradle/api/internal/artifacts/ivyservice/ivyresolve/LoopbackDependencyResolver.java
@@ -17,13 +17,23 @@ package org.gradle.api.internal.artifacts.ivyservice.ivyresolve;
 
 import org.apache.ivy.core.IvyContext;
 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.DefaultDependencyDescriptor;
 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.DependencyResolver;
 import org.apache.ivy.plugins.resolver.ResolverSettings;
+import org.apache.ivy.plugins.resolver.util.ResolvedResource;
 import org.gradle.api.internal.artifacts.ivyservice.CacheLockingManager;
 import org.gradle.api.internal.artifacts.ivyservice.DefaultBuildableArtifactResolveResult;
 import org.gradle.api.internal.artifacts.ivyservice.DefaultBuildableModuleVersionResolveResult;
@@ -31,12 +41,14 @@ import org.gradle.api.internal.artifacts.ivyservice.ModuleVersionNotFoundExcepti
 import org.gradle.internal.Factory;
 
 import java.io.File;
+import java.io.IOException;
 import java.text.ParseException;
+import java.util.Map;
 
 /**
  * The main entry point for a {@link DependencyResolver} to call back into the dependency resolution mechanism.
  */
-public class LoopbackDependencyResolver extends RestrictedDependencyResolver {
+public class LoopbackDependencyResolver implements DependencyResolver {
     private final String name;
     private final UserResolverChain userResolverChain;
     private final CacheLockingManager cacheLockingManager;
@@ -47,17 +59,14 @@ public class LoopbackDependencyResolver extends RestrictedDependencyResolver {
         this.cacheLockingManager = cacheLockingManager;
     }
 
-    @Override
     public String getName() {
         return name;
     }
 
-    @Override
     public void setSettings(ResolverSettings settings) {
         // don't care
     }
 
-    @Override
     public ResolvedModuleRevision getDependency(final DependencyDescriptor dd, final ResolveData data) throws ParseException {
         final DependencyResolver loopback = this;
         return cacheLockingManager.useCache(String.format("Resolve %s", dd), new Factory<ResolvedModuleRevision>() {
@@ -76,7 +85,6 @@ public class LoopbackDependencyResolver extends RestrictedDependencyResolver {
         });
     }
 
-    @Override
     public ArtifactOrigin locate(final Artifact artifact) {
         return cacheLockingManager.useCache(String.format("Locate %s", artifact), new Factory<ArtifactOrigin>() {
             public ArtifactOrigin create() {
@@ -97,4 +105,80 @@ public class LoopbackDependencyResolver extends RestrictedDependencyResolver {
             }
         });
     }
+
+    public void setName(String name) {
+        throw new UnsupportedOperationException();
+    }
+
+    public void abortPublishTransaction() throws IOException {
+        throw new UnsupportedOperationException();
+    }
+
+    public ResolvedResource findIvyFileRef(DependencyDescriptor dd, ResolveData data) {
+        throw new UnsupportedOperationException();
+    }
+
+    public DownloadReport download(Artifact[] artifacts, DownloadOptions options) {
+        throw new UnsupportedOperationException();
+    }
+
+    public ArtifactDownloadReport download(ArtifactOrigin artifact, DownloadOptions options) {
+        throw new UnsupportedOperationException();
+    }
+
+    public boolean exists(Artifact artifact) {
+        throw new UnsupportedOperationException();
+    }
+
+    public void publish(Artifact artifact, File src, boolean overwrite) throws IOException {
+        throw new UnsupportedOperationException();
+    }
+
+    public void beginPublishTransaction(ModuleRevisionId module, boolean overwrite) throws IOException {
+        throw new UnsupportedOperationException();
+    }
+
+    public void commitPublishTransaction() throws IOException {
+        throw new UnsupportedOperationException();
+    }
+
+    public void reportFailure() {
+        throw new UnsupportedOperationException();
+    }
+
+    public void reportFailure(Artifact art) {
+        throw new UnsupportedOperationException();
+    }
+
+    public String[] listTokenValues(String token, Map otherTokenValues) {
+        throw new UnsupportedOperationException();
+    }
+
+    public Map[] listTokenValues(String[] tokens, Map criteria) {
+        throw new UnsupportedOperationException();
+    }
+
+    public OrganisationEntry[] listOrganisations() {
+        throw new UnsupportedOperationException();
+    }
+
+    public ModuleEntry[] listModules(OrganisationEntry org) {
+        throw new UnsupportedOperationException();
+    }
+
+    public RevisionEntry[] listRevisions(ModuleEntry module) {
+        throw new UnsupportedOperationException();
+    }
+
+    public Namespace getNamespace() {
+        throw new UnsupportedOperationException();
+    }
+
+    public void dumpSettings() {
+        throw new UnsupportedOperationException();
+    }
+
+    public RepositoryCacheManager getRepositoryCacheManager() {
+        throw new UnsupportedOperationException();
+    }
 }
diff --git a/subprojects/core-impl/src/main/groovy/org/gradle/api/internal/artifacts/ivyservice/ivyresolve/ModuleSource.java b/subprojects/core-impl/src/main/groovy/org/gradle/api/internal/artifacts/ivyservice/ivyresolve/ModuleSource.java
index 61e2285..a2a980b 100644
--- a/subprojects/core-impl/src/main/groovy/org/gradle/api/internal/artifacts/ivyservice/ivyresolve/ModuleSource.java
+++ b/subprojects/core-impl/src/main/groovy/org/gradle/api/internal/artifacts/ivyservice/ivyresolve/ModuleSource.java
@@ -18,7 +18,11 @@ package org.gradle.api.internal.artifacts.ivyservice.ivyresolve;
 
 import java.io.Serializable;
 
+/**
+ * A memento for any resolution state that is relevant to locate the artifacts of a resolved module version.
+ *
+ * Implementations must retain as little state as possible and must be able to be serialized. Also note that
+ * a given instance may be passed to multiple repository instances.
+ */
 public interface ModuleSource extends Serializable {
 }
-
-
diff --git a/subprojects/core-impl/src/main/groovy/org/gradle/api/internal/artifacts/ivyservice/ivyresolve/ModuleVersionMetaData.java b/subprojects/core-impl/src/main/groovy/org/gradle/api/internal/artifacts/ivyservice/ivyresolve/ModuleVersionMetaData.java
index db1ad9c..d2a24c6 100644
--- a/subprojects/core-impl/src/main/groovy/org/gradle/api/internal/artifacts/ivyservice/ivyresolve/ModuleVersionMetaData.java
+++ b/subprojects/core-impl/src/main/groovy/org/gradle/api/internal/artifacts/ivyservice/ivyresolve/ModuleVersionMetaData.java
@@ -16,12 +16,13 @@
 package org.gradle.api.internal.artifacts.ivyservice.ivyresolve;
 
 import org.apache.ivy.core.module.descriptor.ModuleDescriptor;
+import org.gradle.api.Nullable;
 import org.gradle.api.artifacts.ModuleVersionIdentifier;
 
 import java.util.List;
 
 /**
- * The meta-data for a module version.
+ * The meta-data for a module version required during dependency resolution.
  */
 public interface ModuleVersionMetaData {
     ModuleVersionIdentifier getId();
@@ -30,5 +31,12 @@ public interface ModuleVersionMetaData {
 
     List<DependencyMetaData> getDependencies();
 
+    @Nullable
+    ConfigurationMetaData getConfiguration(String name);
+
     boolean isChanging();
+
+    String getStatus();
+
+    List<String> getStatusScheme();
 }
diff --git a/subprojects/core-impl/src/main/groovy/org/gradle/api/internal/artifacts/ivyservice/ivyresolve/ModuleVersionRepository.java b/subprojects/core-impl/src/main/groovy/org/gradle/api/internal/artifacts/ivyservice/ivyresolve/ModuleVersionRepository.java
index 8b6df7d..41be712 100644
--- a/subprojects/core-impl/src/main/groovy/org/gradle/api/internal/artifacts/ivyservice/ivyresolve/ModuleVersionRepository.java
+++ b/subprojects/core-impl/src/main/groovy/org/gradle/api/internal/artifacts/ivyservice/ivyresolve/ModuleVersionRepository.java
@@ -15,23 +15,27 @@
  */
 package org.gradle.api.internal.artifacts.ivyservice.ivyresolve;
 
-import org.apache.ivy.core.module.descriptor.Artifact;
+import org.gradle.api.artifacts.ArtifactIdentifier;
 import org.gradle.api.internal.artifacts.ivyservice.BuildableArtifactResolveResult;
 
 /**
  * A repository of module versions.
  *
- * The plan is to sync this with {@link org.gradle.api.internal.artifacts.ivyservice.DependencyToModuleResolver}
+ * The plan is to sync this with {@link org.gradle.api.internal.artifacts.ivyservice.DependencyToModuleVersionResolver} and rename it
+ * to have 'resolver' instead of 'repository' in its name.
  */
 public interface ModuleVersionRepository {
     String getId();
 
     String getName();
 
-    void getDependency(DependencyMetaData dependency, BuildableModuleVersionMetaData result);
+    /**
+     * Resolves the given dependency to the corresponding module version meta-data.
+     */
+    void getDependency(DependencyMetaData dependency, BuildableModuleVersionMetaDataResolveResult result);
 
     /**
-     * Downloads the given artifact. Any failures are packaged up in the result.
+     * Resolves the given artifact. Any failures are packaged up in the result.
      */
-    void resolve(Artifact artifact, BuildableArtifactResolveResult result, ModuleSource moduleSource);
+    void resolve(ArtifactIdentifier artifact, BuildableArtifactResolveResult result, ModuleSource moduleSource);
 }
diff --git a/subprojects/core-impl/src/main/groovy/org/gradle/api/internal/artifacts/ivyservice/ivyresolve/MutableModuleVersionMetaData.java b/subprojects/core-impl/src/main/groovy/org/gradle/api/internal/artifacts/ivyservice/ivyresolve/MutableModuleVersionMetaData.java
new file mode 100644
index 0000000..350ca34
--- /dev/null
+++ b/subprojects/core-impl/src/main/groovy/org/gradle/api/internal/artifacts/ivyservice/ivyresolve/MutableModuleVersionMetaData.java
@@ -0,0 +1,24 @@
+/*
+ * Copyright 2013 the original author or authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY 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.ivyresolve;
+
+import java.util.List;
+
+public interface MutableModuleVersionMetaData extends ModuleVersionMetaData {
+    void setChanging(boolean changing);
+    void setStatus(String status);
+    void setStatusScheme(List<String> statusScheme);
+}
diff --git a/subprojects/core-impl/src/main/groovy/org/gradle/api/internal/artifacts/ivyservice/ivyresolve/ResolveIvyFactory.java b/subprojects/core-impl/src/main/groovy/org/gradle/api/internal/artifacts/ivyservice/ivyresolve/ResolveIvyFactory.java
index e672c0e..6174231 100644
--- a/subprojects/core-impl/src/main/groovy/org/gradle/api/internal/artifacts/ivyservice/ivyresolve/ResolveIvyFactory.java
+++ b/subprojects/core-impl/src/main/groovy/org/gradle/api/internal/artifacts/ivyservice/ivyresolve/ResolveIvyFactory.java
@@ -16,63 +16,69 @@
 package org.gradle.api.internal.artifacts.ivyservice.ivyresolve;
 
 import org.apache.ivy.Ivy;
+import org.apache.ivy.core.IvyContext;
 import org.apache.ivy.core.resolve.ResolveData;
 import org.apache.ivy.core.resolve.ResolveOptions;
 import org.apache.ivy.core.settings.IvySettings;
 import org.gradle.api.artifacts.cache.ResolutionRules;
 import org.gradle.api.internal.artifacts.configurations.ConfigurationInternal;
+import org.gradle.api.internal.artifacts.ivyservice.BuildableModuleVersionResolveResult;
 import org.gradle.api.internal.artifacts.ivyservice.CacheLockingManager;
-import org.gradle.api.internal.artifacts.ivyservice.IvyFactory;
-import org.gradle.api.internal.artifacts.ivyservice.SettingsConverter;
+import org.gradle.api.internal.artifacts.ivyservice.DependencyToModuleVersionResolver;
 import org.gradle.api.internal.artifacts.ivyservice.dynamicversions.ModuleResolutionCache;
+import org.gradle.api.internal.artifacts.ivyservice.ivyresolve.memcache.InMemoryDependencyMetadataCache;
+import org.gradle.api.internal.artifacts.ivyservice.ivyresolve.strategy.LatestStrategy;
+import org.gradle.api.internal.artifacts.ivyservice.ivyresolve.strategy.ResolverStrategy;
+import org.gradle.api.internal.artifacts.ivyservice.ivyresolve.strategy.VersionMatcher;
 import org.gradle.api.internal.artifacts.ivyservice.modulecache.ModuleDescriptorCache;
 import org.gradle.api.internal.artifacts.repositories.ResolutionAwareRepository;
+import org.gradle.api.internal.artifacts.repositories.resolver.ExternalResourceResolver;
 import org.gradle.api.internal.externalresource.cached.CachedArtifactIndex;
 import org.gradle.internal.TimeProvider;
 import org.gradle.util.WrapUtil;
 
 public class ResolveIvyFactory {
-    private final IvyFactory ivyFactory;
-    private final SettingsConverter settingsConverter;
     private final ModuleResolutionCache moduleResolutionCache;
     private final ModuleDescriptorCache moduleDescriptorCache;
     private final CachedArtifactIndex artifactAtRepositoryCachedResolutionIndex;
     private final CacheLockingManager cacheLockingManager;
     private final StartParameterResolutionOverride startParameterResolutionOverride;
     private final TimeProvider timeProvider;
+    private InMemoryDependencyMetadataCache inMemoryCache;
 
-    public ResolveIvyFactory(IvyFactory ivyFactory, SettingsConverter settingsConverter,
-                             ModuleResolutionCache moduleResolutionCache, ModuleDescriptorCache moduleDescriptorCache,
+    public ResolveIvyFactory(ModuleResolutionCache moduleResolutionCache, ModuleDescriptorCache moduleDescriptorCache,
                              CachedArtifactIndex artifactAtRepositoryCachedResolutionIndex,
                              CacheLockingManager cacheLockingManager, StartParameterResolutionOverride startParameterResolutionOverride,
-                             TimeProvider timeProvider) {
-        this.ivyFactory = ivyFactory;
-        this.settingsConverter = settingsConverter;
+                             TimeProvider timeProvider, InMemoryDependencyMetadataCache inMemoryCache) {
         this.moduleResolutionCache = moduleResolutionCache;
         this.moduleDescriptorCache = moduleDescriptorCache;
         this.artifactAtRepositoryCachedResolutionIndex = artifactAtRepositoryCachedResolutionIndex;
         this.cacheLockingManager = cacheLockingManager;
         this.startParameterResolutionOverride = startParameterResolutionOverride;
         this.timeProvider = timeProvider;
+        this.inMemoryCache = inMemoryCache;
     }
 
     public IvyAdapter create(ConfigurationInternal configuration, Iterable<? extends ResolutionAwareRepository> repositories) {
-        UserResolverChain userResolverChain = new UserResolverChain();
         ResolutionRules resolutionRules = configuration.getResolutionStrategy().getResolutionRules();
         startParameterResolutionOverride.addResolutionRules(resolutionRules);
 
-        LoopbackDependencyResolver loopbackDependencyResolver = new LoopbackDependencyResolver(SettingsConverter.LOOPBACK_RESOLVER_NAME, userResolverChain, cacheLockingManager);
+        VersionMatcher versionMatcher = ResolverStrategy.INSTANCE.getVersionMatcher();
+        LatestStrategy comparatorLatestStrategy = ResolverStrategy.INSTANCE.getLatestStrategy();
 
-        IvySettings ivySettings = settingsConverter.convertForResolve(loopbackDependencyResolver);
-        userResolverChain.setSettings(ivySettings);
-
-        Ivy ivy = ivyFactory.createIvy(ivySettings);
-        ResolveData resolveData = createResolveData(ivy, configuration.getName());
-        IvyContextualiser contextualiser = new IvyContextualiser(ivy, resolveData);
+        UserResolverChain userResolverChain = new UserResolverChain(versionMatcher, comparatorLatestStrategy);
+        DependencyToModuleVersionResolver parentLookupResolver = new ParentModuleLookupResolver(userResolverChain, cacheLockingManager);
 
         for (ResolutionAwareRepository repository : repositories) {
-            IvyAwareModuleVersionRepository moduleVersionRepository = repository.createResolver();
-            moduleVersionRepository.setSettings(ivySettings);
+            ConfiguredModuleVersionRepository moduleVersionRepository = repository.createResolver();
+
+            if (moduleVersionRepository instanceof IvyAwareModuleVersionRepository) {
+                ivyContextualize((IvyAwareModuleVersionRepository) moduleVersionRepository, userResolverChain, configuration.getName());
+            }
+            if (moduleVersionRepository instanceof ExternalResourceResolver) {
+                // TODO:DAZ Should have type for this
+                ((ExternalResourceResolver) moduleVersionRepository).setResolver(parentLookupResolver);
+            }
 
             LocalAwareModuleVersionRepository localAwareRepository;
             if (moduleVersionRepository.isLocal()) {
@@ -81,16 +87,28 @@ public class ResolveIvyFactory {
                 ModuleVersionRepository wrapperRepository = new CacheLockingModuleVersionRepository(moduleVersionRepository, cacheLockingManager);
                 wrapperRepository = startParameterResolutionOverride.overrideModuleVersionRepository(wrapperRepository);
                 localAwareRepository = new CachingModuleVersionRepository(wrapperRepository, moduleResolutionCache, moduleDescriptorCache, artifactAtRepositoryCachedResolutionIndex,
-                        configuration.getResolutionStrategy().getCachePolicy(), timeProvider);
+                        parentLookupResolver, configuration.getResolutionStrategy().getCachePolicy(), timeProvider);
             }
             if (moduleVersionRepository.isDynamicResolveMode()) {
                 localAwareRepository = new IvyDynamicResolveModuleVersionRepository(localAwareRepository);
             }
-            localAwareRepository = contextualiser.contextualise(LocalAwareModuleVersionRepository.class, localAwareRepository);
+            localAwareRepository = inMemoryCache.cached(localAwareRepository);
             userResolverChain.add(localAwareRepository);
         }
 
-        return new DefaultIvyAdapter(resolveData, userResolverChain);
+        return new DefaultIvyAdapter(versionMatcher, comparatorLatestStrategy, userResolverChain);
+    }
+
+    private void ivyContextualize(IvyAwareModuleVersionRepository ivyAwareRepository, UserResolverChain userResolverChain, String configurationName) {
+        Ivy ivy = IvyContext.getContext().getIvy();
+        IvySettings ivySettings = ivy.getSettings();
+        LoopbackDependencyResolver loopbackDependencyResolver = new LoopbackDependencyResolver("main", userResolverChain, cacheLockingManager);
+        ivySettings.addResolver(loopbackDependencyResolver);
+        ivySettings.setDefaultResolver(loopbackDependencyResolver.getName());
+
+        ResolveData resolveData = createResolveData(ivy, configurationName);
+        ivyAwareRepository.setSettings(ivySettings);
+        ivyAwareRepository.setResolveData(resolveData);
     }
 
     private ResolveData createResolveData(Ivy ivy, String configurationName) {
@@ -99,4 +117,25 @@ public class ResolveIvyFactory {
         options.setConfs(WrapUtil.toArray(configurationName));
         return new ResolveData(ivy.getResolveEngine(), options);
     }
+
+    /**
+     * Provides access to the top-level resolver chain for looking up parent modules when parsing module descriptor files.
+     */
+    private static class ParentModuleLookupResolver implements DependencyToModuleVersionResolver {
+        private final UserResolverChain delegate;
+        private final CacheLockingManager cacheLockingManager;
+
+        public ParentModuleLookupResolver(UserResolverChain delegate, CacheLockingManager cacheLockingManager) {
+            this.delegate = delegate;
+            this.cacheLockingManager = cacheLockingManager;
+        }
+
+        public void resolve(final DependencyMetaData dependency, final BuildableModuleVersionResolveResult result) {
+            cacheLockingManager.useCache(String.format("Resolve %s", dependency), new Runnable() {
+                public void run() {
+                    delegate.resolve(dependency, result);
+                }
+            });
+        }
+    }
 }
diff --git a/subprojects/core-impl/src/main/groovy/org/gradle/api/internal/artifacts/ivyservice/ivyresolve/RestrictedDependencyResolver.java b/subprojects/core-impl/src/main/groovy/org/gradle/api/internal/artifacts/ivyservice/ivyresolve/RestrictedDependencyResolver.java
deleted file mode 100644
index fc308e2..0000000
--- a/subprojects/core-impl/src/main/groovy/org/gradle/api/internal/artifacts/ivyservice/ivyresolve/RestrictedDependencyResolver.java
+++ /dev/null
@@ -1,36 +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.api.internal.artifacts.ivyservice.ivyresolve;
-
-import org.apache.ivy.plugins.resolver.DependencyResolver;
-
-import java.lang.reflect.InvocationHandler;
-import java.lang.reflect.Method;
-import java.lang.reflect.Proxy;
-
-public abstract class RestrictedDependencyResolver extends DelegatingDependencyResolver {
-    protected RestrictedDependencyResolver() {
-        super(createAngryDelegate());
-    }
-
-    private static DependencyResolver createAngryDelegate() {
-        return (DependencyResolver) Proxy.newProxyInstance(RestrictedDependencyResolver.class.getClassLoader(), new Class<?>[]{DependencyResolver.class}, new InvocationHandler() {
-            public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
-                throw new UnsupportedOperationException();
-            }
-        });
-    }
-}
diff --git a/subprojects/core-impl/src/main/groovy/org/gradle/api/internal/artifacts/ivyservice/ivyresolve/StartParameterResolutionOverride.java b/subprojects/core-impl/src/main/groovy/org/gradle/api/internal/artifacts/ivyservice/ivyresolve/StartParameterResolutionOverride.java
index b75aeec..ba82e2f 100644
--- a/subprojects/core-impl/src/main/groovy/org/gradle/api/internal/artifacts/ivyservice/ivyresolve/StartParameterResolutionOverride.java
+++ b/subprojects/core-impl/src/main/groovy/org/gradle/api/internal/artifacts/ivyservice/ivyresolve/StartParameterResolutionOverride.java
@@ -15,9 +15,9 @@
  */
 package org.gradle.api.internal.artifacts.ivyservice.ivyresolve;
 
-import org.apache.ivy.core.module.descriptor.Artifact;
 import org.gradle.StartParameter;
 import org.gradle.api.Action;
+import org.gradle.api.artifacts.ArtifactIdentifier;
 import org.gradle.api.artifacts.cache.ArtifactResolutionControl;
 import org.gradle.api.artifacts.cache.DependencyResolutionControl;
 import org.gradle.api.artifacts.cache.ModuleResolutionControl;
@@ -96,11 +96,11 @@ public class StartParameterResolutionOverride {
             return false;
         }
 
-        public void getDependency(DependencyMetaData dependency, BuildableModuleVersionMetaData result) {
+        public void getDependency(DependencyMetaData dependency, BuildableModuleVersionMetaDataResolveResult result) {
             result.failed(new ModuleVersionResolveException(dependency.getRequested(), "No cached version of %s available for offline mode."));
         }
 
-        public void resolve(Artifact artifact, BuildableArtifactResolveResult result, ModuleSource moduleSource) {
+        public void resolve(ArtifactIdentifier artifact, BuildableArtifactResolveResult result, ModuleSource moduleSource) {
             result.failed(new ArtifactResolveException(artifact, "No cached version available for offline mode"));
         }
     }
diff --git a/subprojects/core-impl/src/main/groovy/org/gradle/api/internal/artifacts/ivyservice/ivyresolve/UserResolverChain.java b/subprojects/core-impl/src/main/groovy/org/gradle/api/internal/artifacts/ivyservice/ivyresolve/UserResolverChain.java
index 0364650..1507018 100644
--- a/subprojects/core-impl/src/main/groovy/org/gradle/api/internal/artifacts/ivyservice/ivyresolve/UserResolverChain.java
+++ b/subprojects/core-impl/src/main/groovy/org/gradle/api/internal/artifacts/ivyservice/ivyresolve/UserResolverChain.java
@@ -17,25 +17,30 @@
 package org.gradle.api.internal.artifacts.ivyservice.ivyresolve;
 
 import org.apache.ivy.core.module.descriptor.Artifact;
-import org.apache.ivy.plugins.latest.ArtifactInfo;
-import org.apache.ivy.plugins.latest.ComparatorLatestStrategy;
-import org.apache.ivy.plugins.resolver.ResolverSettings;
 import org.gradle.api.artifacts.ModuleVersionSelector;
+import org.gradle.api.internal.artifacts.DefaultArtifactIdentifier;
 import org.gradle.api.internal.artifacts.ivyservice.*;
+import org.gradle.api.internal.artifacts.ivyservice.ivyresolve.strategy.LatestStrategy;
+import org.gradle.api.internal.artifacts.ivyservice.ivyresolve.strategy.VersionMatcher;
 import org.slf4j.Logger;
 import org.slf4j.LoggerFactory;
 
-import java.util.*;
+import java.util.ArrayList;
+import java.util.Collection;
+import java.util.LinkedList;
+import java.util.List;
 
-public class UserResolverChain implements DependencyToModuleResolver {
+public class UserResolverChain implements DependencyToModuleVersionResolver {
     private static final Logger LOGGER = LoggerFactory.getLogger(UserResolverChain.class);
 
     private final List<LocalAwareModuleVersionRepository> moduleVersionRepositories = new ArrayList<LocalAwareModuleVersionRepository>();
     private final List<String> moduleVersionRepositoryNames = new ArrayList<String>();
-    private ResolverSettings settings;
+    private final VersionMatcher versionMatcher;
+    private final LatestStrategy latestStrategy;
 
-    public void setSettings(ResolverSettings settings) {
-        this.settings = settings;
+    public UserResolverChain(VersionMatcher versionMatcher, LatestStrategy latestStrategy) {
+        this.versionMatcher = versionMatcher;
+        this.latestStrategy = latestStrategy;
     }
 
     public void add(LocalAwareModuleVersionRepository repository) {
@@ -84,7 +89,7 @@ public class UserResolverChain implements DependencyToModuleResolver {
     }
 
     private ModuleResolution findLatestModule(DependencyMetaData dependency, LinkedList<RepositoryResolveState> queue, Collection<Throwable> failures, Collection<RepositoryResolveState> missing) {
-        boolean isStaticVersion = !settings.getVersionMatcher().isDynamic(dependency.getDescriptor().getDependencyRevisionId());
+        boolean isStaticVersion = !versionMatcher.isDynamic(dependency.getRequested().getVersion());
         ModuleResolution best = null;
         while (!queue.isEmpty()) {
             RepositoryResolveState request = queue.removeFirst();
@@ -110,7 +115,7 @@ public class UserResolverChain implements DependencyToModuleResolver {
                     }
                     break;
                 case Resolved:
-                    ModuleResolution moduleResolution = new ModuleResolution(request.repository, request.descriptor, request.descriptor.getModuleSource());
+                    ModuleResolution moduleResolution = new ModuleResolution(request.repository, request.descriptor.getMetaData(), request.descriptor.getModuleSource());
                     if (isStaticVersion && !moduleResolution.isGeneratedModuleDescriptor()) {
                         return moduleResolution;
                     }
@@ -132,9 +137,7 @@ public class UserResolverChain implements DependencyToModuleResolver {
             return two.module == null ? one : two;
         }
 
-        ComparatorLatestStrategy latestStrategy = (ComparatorLatestStrategy) settings.getDefaultLatestStrategy();
-        Comparator<ArtifactInfo> comparator = latestStrategy.getComparator();
-        int comparison = comparator.compare(one, two);
+        int comparison = latestStrategy.compare(one, two);
 
         if (comparison == 0) {
             if (one.isGeneratedModuleDescriptor() && !two.isGeneratedModuleDescriptor()) {
@@ -156,13 +159,13 @@ public class UserResolverChain implements DependencyToModuleResolver {
         }
 
         public void resolve(Artifact artifact, BuildableArtifactResolveResult result) {
-            delegate.resolve(artifact, result, moduleSource);
+            delegate.resolve(new DefaultArtifactIdentifier(artifact), result, moduleSource);
         }
     }
 
     private static class RepositoryResolveState {
         final LocalAwareModuleVersionRepository repository;
-        final DefaultBuildableModuleVersionMetaData descriptor = new DefaultBuildableModuleVersionMetaData();
+        final DefaultBuildableModuleVersionMetaDataResolveResult descriptor = new DefaultBuildableModuleVersionMetaDataResolveResult();
 
         boolean searchedLocally;
         boolean searchedRemotely;
@@ -179,7 +182,7 @@ public class UserResolverChain implements DependencyToModuleResolver {
                 searchedRemotely = true;
                 repository.getDependency(dependency, descriptor);
             }
-            if (descriptor.getState() == BuildableModuleVersionMetaData.State.Failed) {
+            if (descriptor.getState() == BuildableModuleVersionMetaDataResolveResult.State.Failed) {
                 throw descriptor.getFailure();
             }
         }
@@ -189,7 +192,7 @@ public class UserResolverChain implements DependencyToModuleResolver {
         }
     }
 
-    private static class ModuleResolution implements ArtifactInfo {
+    private static class ModuleResolution implements Versioned {
         public final ModuleVersionRepository repository;
         public final ModuleVersionMetaData module;
         public final ModuleSource moduleSource;
@@ -204,11 +207,7 @@ public class UserResolverChain implements DependencyToModuleResolver {
             return module.getDescriptor().isDefault();
         }
 
-        public long getLastModified() {
-            return module.getDescriptor().getResolvedPublicationDate().getTime();
-        }
-
-        public String getRevision() {
+        public String getVersion() {
             return module.getId().getVersion();
         }
     }
diff --git a/subprojects/core-impl/src/main/groovy/org/gradle/api/internal/artifacts/ivyservice/ivyresolve/VersionInfo.java b/subprojects/core-impl/src/main/groovy/org/gradle/api/internal/artifacts/ivyservice/ivyresolve/VersionInfo.java
new file mode 100644
index 0000000..8ba0d75
--- /dev/null
+++ b/subprojects/core-impl/src/main/groovy/org/gradle/api/internal/artifacts/ivyservice/ivyresolve/VersionInfo.java
@@ -0,0 +1,39 @@
+/*
+ * Copyright 2013 the original author or authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY 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.ivyresolve;
+
+public class VersionInfo implements Versioned {
+    private final String version;
+
+    public VersionInfo(String version) {
+        this.version = version;
+    }
+
+    public String getVersion() {
+        return version;
+    }
+
+    public boolean equals(Object other) {
+        if (!(other instanceof VersionInfo)) {
+            return false;
+        }
+        return version.equals(((VersionInfo) other).getVersion());
+    }
+
+    public int hashCode() {
+        return version.hashCode();
+    }
+}
diff --git a/subprojects/core-impl/src/main/groovy/org/gradle/api/internal/artifacts/ivyservice/ivyresolve/Versioned.java b/subprojects/core-impl/src/main/groovy/org/gradle/api/internal/artifacts/ivyservice/ivyresolve/Versioned.java
new file mode 100644
index 0000000..7a4e207
--- /dev/null
+++ b/subprojects/core-impl/src/main/groovy/org/gradle/api/internal/artifacts/ivyservice/ivyresolve/Versioned.java
@@ -0,0 +1,20 @@
+/*
+ * Copyright 2013 the original author or authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY 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.ivyresolve;
+
+public interface Versioned {
+    String getVersion();
+}
diff --git a/subprojects/core-impl/src/main/groovy/org/gradle/api/internal/artifacts/ivyservice/ivyresolve/memcache/CachedModuleVersionResult.java b/subprojects/core-impl/src/main/groovy/org/gradle/api/internal/artifacts/ivyservice/ivyresolve/memcache/CachedModuleVersionResult.java
new file mode 100644
index 0000000..d239657
--- /dev/null
+++ b/subprojects/core-impl/src/main/groovy/org/gradle/api/internal/artifacts/ivyservice/ivyresolve/memcache/CachedModuleVersionResult.java
@@ -0,0 +1,64 @@
+/*
+ * Copyright 2013 the original author or authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY 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.ivyresolve.memcache;
+
+import org.apache.ivy.core.module.descriptor.ModuleDescriptor;
+import org.gradle.api.artifacts.ModuleVersionIdentifier;
+import org.gradle.api.internal.artifacts.ivyservice.ivyresolve.BuildableModuleVersionMetaDataResolveResult;
+import org.gradle.api.internal.artifacts.ivyservice.ivyresolve.ModuleSource;
+import org.gradle.api.internal.artifacts.ivyservice.ivyresolve.ModuleVersionMetaData;
+
+import static org.gradle.api.internal.artifacts.ivyservice.ivyresolve.BuildableModuleVersionMetaDataResolveResult.State.*;
+
+class CachedModuleVersionResult {
+    private final BuildableModuleVersionMetaDataResolveResult.State state;
+    private final ModuleDescriptor moduleDescriptor;
+    private final boolean isChanging;
+    private final ModuleSource moduleSource;
+    private final ModuleVersionIdentifier id;
+
+    public CachedModuleVersionResult(BuildableModuleVersionMetaDataResolveResult result) {
+        this.state = result.getState();
+        if (state == Resolved) {
+            ModuleVersionMetaData metaData = result.getMetaData();
+            this.id = metaData.getId();
+            this.moduleDescriptor = metaData.getDescriptor();
+            this.isChanging = metaData.isChanging();
+            this.moduleSource = result.getModuleSource();
+        } else {
+            this.id = null;
+            this.moduleDescriptor = null;
+            this.isChanging = false;
+            this.moduleSource = null;
+        }
+    }
+
+    public boolean isCacheable() {
+        return state == Missing || state == ProbablyMissing || state == Resolved;
+    }
+
+    public void supply(BuildableModuleVersionMetaDataResolveResult result) {
+        assert isCacheable() : "Results are not cacheable, cannot supply the results.";
+        if (state == Resolved) {
+            result.resolved(id, moduleDescriptor, isChanging, moduleSource);
+        } else if (state == Missing) {
+            result.missing();
+        } else if (state == ProbablyMissing) {
+            result.probablyMissing();
+        }
+    }
+}
diff --git a/subprojects/core-impl/src/main/groovy/org/gradle/api/internal/artifacts/ivyservice/ivyresolve/memcache/CachedRepository.java b/subprojects/core-impl/src/main/groovy/org/gradle/api/internal/artifacts/ivyservice/ivyresolve/memcache/CachedRepository.java
new file mode 100644
index 0000000..1e45b2e
--- /dev/null
+++ b/subprojects/core-impl/src/main/groovy/org/gradle/api/internal/artifacts/ivyservice/ivyresolve/memcache/CachedRepository.java
@@ -0,0 +1,65 @@
+/*
+ * Copyright 2013 the original author or authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY 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.ivyresolve.memcache;
+
+import org.gradle.api.artifacts.ArtifactIdentifier;
+import org.gradle.api.internal.artifacts.ivyservice.BuildableArtifactResolveResult;
+import org.gradle.api.internal.artifacts.ivyservice.ivyresolve.BuildableModuleVersionMetaDataResolveResult;
+import org.gradle.api.internal.artifacts.ivyservice.ivyresolve.DependencyMetaData;
+import org.gradle.api.internal.artifacts.ivyservice.ivyresolve.LocalAwareModuleVersionRepository;
+import org.gradle.api.internal.artifacts.ivyservice.ivyresolve.ModuleSource;
+
+class CachedRepository implements LocalAwareModuleVersionRepository {
+    final DependencyMetadataCache cache;
+    final LocalAwareModuleVersionRepository delegate;
+    final DependencyMetadataCacheStats stats;
+
+    public CachedRepository(DependencyMetadataCache cache, LocalAwareModuleVersionRepository delegate, DependencyMetadataCacheStats stats) {
+        this.cache = cache;
+        this.delegate = delegate;
+        this.stats = stats;
+    }
+
+    public String getId() {
+        return delegate.getId();
+    }
+
+    public String getName() {
+        return delegate.getName();
+    }
+
+    public void getLocalDependency(DependencyMetaData dependency, BuildableModuleVersionMetaDataResolveResult result) {
+        if(!cache.supplyLocalMetaData(dependency.getRequested(), result)) {
+            delegate.getLocalDependency(dependency, result);
+            cache.newLocalDependencyResult(dependency.getRequested(), result);
+        }
+    }
+
+    public void getDependency(DependencyMetaData dependency, BuildableModuleVersionMetaDataResolveResult result) {
+        if(!cache.supplyMetaData(dependency.getRequested(), result)) {
+            delegate.getDependency(dependency, result);
+            cache.newDependencyResult(dependency.getRequested(), result);
+        }
+    }
+
+    public void resolve(ArtifactIdentifier artifact, BuildableArtifactResolveResult result, ModuleSource moduleSource) {
+        if(!cache.supplyArtifact(artifact, result)) {
+            delegate.resolve(artifact, result, moduleSource);
+            cache.newArtifact(artifact, result);
+        }
+    }
+}
diff --git a/subprojects/core-impl/src/main/groovy/org/gradle/api/internal/artifacts/ivyservice/ivyresolve/memcache/DependencyMetadataCache.java b/subprojects/core-impl/src/main/groovy/org/gradle/api/internal/artifacts/ivyservice/ivyresolve/memcache/DependencyMetadataCache.java
new file mode 100644
index 0000000..bed91f9
--- /dev/null
+++ b/subprojects/core-impl/src/main/groovy/org/gradle/api/internal/artifacts/ivyservice/ivyresolve/memcache/DependencyMetadataCache.java
@@ -0,0 +1,84 @@
+/*
+ * Copyright 2013 the original author or authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY 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.ivyresolve.memcache;
+
+import org.gradle.api.artifacts.ArtifactIdentifier;
+import org.gradle.api.artifacts.ModuleVersionSelector;
+import org.gradle.api.internal.artifacts.ivyservice.BuildableArtifactResolveResult;
+import org.gradle.api.internal.artifacts.ivyservice.ivyresolve.BuildableModuleVersionMetaDataResolveResult;
+
+import java.io.File;
+import java.util.HashMap;
+import java.util.Map;
+
+class DependencyMetadataCache {
+    private final Map<ModuleVersionSelector, CachedModuleVersionResult> localMetaData = new HashMap<ModuleVersionSelector, CachedModuleVersionResult>();
+    private final Map<ModuleVersionSelector, CachedModuleVersionResult> metaData = new HashMap<ModuleVersionSelector, CachedModuleVersionResult>();
+    private final Map<ArtifactIdentifier, File> artifacts = new HashMap<ArtifactIdentifier, File>();
+    private DependencyMetadataCacheStats stats;
+
+    DependencyMetadataCache(DependencyMetadataCacheStats stats) {
+        this.stats = stats;
+    }
+
+    boolean supplyLocalMetaData(ModuleVersionSelector requested, BuildableModuleVersionMetaDataResolveResult result) {
+        return supply(requested, result, localMetaData, stats);
+    }
+
+    boolean supplyMetaData(ModuleVersionSelector requested, BuildableModuleVersionMetaDataResolveResult result) {
+        return supply(requested, result, metaData, stats);
+    }
+
+    private static boolean supply(ModuleVersionSelector requested, BuildableModuleVersionMetaDataResolveResult result, Map<ModuleVersionSelector, CachedModuleVersionResult> map, DependencyMetadataCacheStats stats) {
+        CachedModuleVersionResult fromCache = map.get(requested);
+        if (fromCache == null) {
+            return false;
+        }
+        fromCache.supply(result);
+        stats.metadataServed++;
+        return true;
+    }
+
+    void newLocalDependencyResult(ModuleVersionSelector requested, BuildableModuleVersionMetaDataResolveResult result) {
+        newResult(requested, result, localMetaData);
+    }
+
+    void newDependencyResult(ModuleVersionSelector requested, BuildableModuleVersionMetaDataResolveResult result) {
+        newResult(requested, result, metaData);
+    }
+
+    private static void newResult(ModuleVersionSelector requested, BuildableModuleVersionMetaDataResolveResult result, Map<ModuleVersionSelector, CachedModuleVersionResult> map) {
+        CachedModuleVersionResult cachedResult = new CachedModuleVersionResult(result);
+        if (cachedResult.isCacheable()) {
+            map.put(requested, cachedResult);
+        }
+    }
+
+    public boolean supplyArtifact(ArtifactIdentifier id, BuildableArtifactResolveResult result) {
+        File fromCache = artifacts.get(id);
+        if (fromCache != null) {
+            result.resolved(fromCache);
+            stats.artifactsServed++;
+            return true;
+        }
+        return false;
+    }
+
+    public void newArtifact(ArtifactIdentifier id, BuildableArtifactResolveResult result) {
+        artifacts.put(id, result.getFile());
+    }
+}
\ No newline at end of file
diff --git a/subprojects/core-impl/src/main/groovy/org/gradle/api/internal/artifacts/ivyservice/ivyresolve/memcache/DependencyMetadataCacheStats.java b/subprojects/core-impl/src/main/groovy/org/gradle/api/internal/artifacts/ivyservice/ivyresolve/memcache/DependencyMetadataCacheStats.java
new file mode 100644
index 0000000..127d5e6
--- /dev/null
+++ b/subprojects/core-impl/src/main/groovy/org/gradle/api/internal/artifacts/ivyservice/ivyresolve/memcache/DependencyMetadataCacheStats.java
@@ -0,0 +1,29 @@
+/*
+ * Copyright 2013 the original author or authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY 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.ivyresolve.memcache;
+
+class DependencyMetadataCacheStats {
+    int cacheInstances;
+    int reposWrapped;
+    int metadataServed;
+    int artifactsServed;
+    public String toString() {
+        return String.format(
+                "Repos cached: %s, cache instances: %s, modules served from cache: %s, artifacts: %s",
+                reposWrapped, cacheInstances, metadataServed, artifactsServed);
+    }
+}
diff --git a/subprojects/core-impl/src/main/groovy/org/gradle/api/internal/artifacts/ivyservice/ivyresolve/memcache/InMemoryDependencyMetadataCache.java b/subprojects/core-impl/src/main/groovy/org/gradle/api/internal/artifacts/ivyservice/ivyresolve/memcache/InMemoryDependencyMetadataCache.java
new file mode 100644
index 0000000..120d6e1
--- /dev/null
+++ b/subprojects/core-impl/src/main/groovy/org/gradle/api/internal/artifacts/ivyservice/ivyresolve/memcache/InMemoryDependencyMetadataCache.java
@@ -0,0 +1,62 @@
+/*
+ * Copyright 2013 the original author or authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY 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.ivyresolve.memcache;
+
+import com.google.common.collect.MapMaker;
+import org.gradle.api.internal.artifacts.ivyservice.ivyresolve.LocalAwareModuleVersionRepository;
+import org.gradle.api.logging.Logger;
+import org.gradle.api.logging.Logging;
+import org.gradle.internal.Stoppable;
+
+import java.util.Map;
+
+/**
+ * Caches the dependency metadata (descriptors, artifact files) in memory. Uses soft maps to reduce heap pressure.
+ */
+public class InMemoryDependencyMetadataCache implements Stoppable {
+
+    public final static String TOGGLE_PROPERTY = "org.gradle.resolution.memorycache";
+
+    private final static Logger LOG = Logging.getLogger(InMemoryDependencyMetadataCache.class);
+
+    Map<String, DependencyMetadataCache> cachePerRepo = new MapMaker().softValues().makeMap();
+
+    final DependencyMetadataCacheStats stats = new DependencyMetadataCacheStats();
+
+    public LocalAwareModuleVersionRepository cached(LocalAwareModuleVersionRepository input) {
+        if ("false".equalsIgnoreCase(System.getProperty(TOGGLE_PROPERTY))) {
+            return input;
+        }
+
+        DependencyMetadataCache dataCache = cachePerRepo.get(input.getId());
+        stats.reposWrapped++;
+        if (dataCache == null) {
+            LOG.debug("Creating new in-memory cache for repo '{}' [{}].", input.getName(), input.getId());
+            dataCache = new DependencyMetadataCache(stats);
+            stats.cacheInstances++;
+            cachePerRepo.put(input.getId(), dataCache);
+        } else {
+            LOG.debug("Reusing in-memory cache for repo '{}' [{}].", input.getName(), input.getId());
+        }
+        return new CachedRepository(dataCache, input, stats);
+    }
+
+    public void stop() {
+        cachePerRepo.clear();
+        LOG.debug("In-memory dependency metadata cache closed. {}", stats);
+    }
+}
diff --git a/subprojects/core-impl/src/main/groovy/org/gradle/api/internal/artifacts/ivyservice/ivyresolve/parser/AbstractDescriptorParseContext.java b/subprojects/core-impl/src/main/groovy/org/gradle/api/internal/artifacts/ivyservice/ivyresolve/parser/AbstractDescriptorParseContext.java
new file mode 100644
index 0000000..e0b97eb
--- /dev/null
+++ b/subprojects/core-impl/src/main/groovy/org/gradle/api/internal/artifacts/ivyservice/ivyresolve/parser/AbstractDescriptorParseContext.java
@@ -0,0 +1,66 @@
+/*
+ * Copyright 2013 the original author or authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY 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.ivyresolve.parser;
+
+import org.apache.ivy.core.IvyPatternHelper;
+import org.apache.ivy.plugins.matcher.PatternMatcher;
+import org.gradle.api.Transformer;
+import org.gradle.api.internal.artifacts.ivyservice.ivyresolve.strategy.ResolverStrategy;
+import org.gradle.util.CollectionUtils;
+
+import java.io.File;
+import java.util.HashMap;
+import java.util.Map;
+import java.util.Set;
+
+public abstract class AbstractDescriptorParseContext implements DescriptorParseContext {
+    protected final String defaultStatus;
+    private final Map<String, String> properties = new HashMap<String, String>();
+
+    public AbstractDescriptorParseContext(String defaultStatus) {
+        this.defaultStatus = defaultStatus;
+        populateProperties();
+    }
+
+    private void populateProperties() {
+        String baseDir = new File(".").getAbsolutePath();
+        properties.put("ivy.default.settings.dir", baseDir);
+        properties.put("ivy.basedir", baseDir);
+
+        Set<String> propertyNames = CollectionUtils.collect(System.getProperties().entrySet(), new Transformer<String, Map.Entry<Object, Object>>() {
+            public String transform(Map.Entry<Object, Object> entry) {
+                return entry.getKey().toString();
+            }
+        });
+
+        for (String property : propertyNames) {
+            properties.put(property, System.getProperty(property));
+        }
+    }
+
+    public String substitute(String value) {
+        return IvyPatternHelper.substituteVariables(value, properties);
+    }
+
+    public PatternMatcher getMatcher(String matcherName) {
+        return ResolverStrategy.INSTANCE.getPatternMatcher(matcherName);
+    }
+
+    public String getDefaultStatus() {
+        return defaultStatus;
+    }
+}
diff --git a/subprojects/core-impl/src/main/groovy/org/gradle/api/internal/artifacts/ivyservice/ivyresolve/parser/AbstractModuleDescriptorParser.java b/subprojects/core-impl/src/main/groovy/org/gradle/api/internal/artifacts/ivyservice/ivyresolve/parser/AbstractModuleDescriptorParser.java
new file mode 100644
index 0000000..3e59d0f
--- /dev/null
+++ b/subprojects/core-impl/src/main/groovy/org/gradle/api/internal/artifacts/ivyservice/ivyresolve/parser/AbstractModuleDescriptorParser.java
@@ -0,0 +1,48 @@
+/*
+ * Copyright 2013 the original author or authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY 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.ivyresolve.parser;
+
+import org.apache.ivy.core.module.descriptor.DefaultModuleDescriptor;
+import org.apache.ivy.core.module.descriptor.ModuleDescriptor;
+import org.gradle.api.internal.externalresource.DefaultLocallyAvailableExternalResource;
+import org.gradle.api.internal.externalresource.LocallyAvailableExternalResource;
+import org.gradle.internal.resource.local.DefaultLocallyAvailableResource;
+import org.gradle.internal.resource.local.LocallyAvailableResource;
+
+import java.io.File;
+
+public abstract class AbstractModuleDescriptorParser implements ModuleDescriptorParser {
+    public ModuleDescriptor parseDescriptor(DescriptorParseContext ivySettings, File descriptorFile, boolean validate) throws MetaDataParseException {
+        LocallyAvailableResource localResource = new DefaultLocallyAvailableResource(descriptorFile);
+        LocallyAvailableExternalResource resource = new DefaultLocallyAvailableExternalResource(descriptorFile.toURI().toString(), localResource);
+        return parseDescriptor(ivySettings, resource, validate);
+    }
+
+    public DefaultModuleDescriptor parseDescriptor(DescriptorParseContext ivySettings, LocallyAvailableExternalResource resource, boolean validate) throws MetaDataParseException {
+        try {
+            return doParseDescriptor(ivySettings, resource, validate);
+        } catch (MetaDataParseException e) {
+            throw e;
+        } catch (Exception e) {
+            throw new MetaDataParseException(getTypeName(), resource, e);
+        }
+    }
+
+    protected abstract String getTypeName();
+
+    protected abstract DefaultModuleDescriptor doParseDescriptor(DescriptorParseContext ivySettings, LocallyAvailableExternalResource resource, boolean validate) throws Exception;
+}
diff --git a/subprojects/core-impl/src/main/groovy/org/gradle/api/internal/artifacts/ivyservice/ivyresolve/parser/DefaultMetaDataParser.java b/subprojects/core-impl/src/main/groovy/org/gradle/api/internal/artifacts/ivyservice/ivyresolve/parser/DefaultMetaDataParser.java
new file mode 100644
index 0000000..b350a05
--- /dev/null
+++ b/subprojects/core-impl/src/main/groovy/org/gradle/api/internal/artifacts/ivyservice/ivyresolve/parser/DefaultMetaDataParser.java
@@ -0,0 +1,34 @@
+/*
+ * Copyright 2013 the original author or authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY 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.ivyresolve.parser;
+
+import org.apache.ivy.core.module.descriptor.ModuleDescriptor;
+import org.gradle.api.internal.artifacts.ivyservice.ivyresolve.MutableModuleVersionMetaData;
+import org.gradle.api.internal.externalresource.LocallyAvailableExternalResource;
+
+public class DefaultMetaDataParser implements MetaDataParser {
+    private final ParserRegistry parserRegistry;
+
+    public DefaultMetaDataParser(ParserRegistry parserRegistry) {
+        this.parserRegistry = parserRegistry;
+    }
+
+    public MutableModuleVersionMetaData parseModuleMetaData(LocallyAvailableExternalResource resource, DescriptorParseContext context) throws MetaDataParseException {
+        ModuleDescriptorParser parser = parserRegistry.forResource(resource);
+        ModuleDescriptor moduleDescriptor = parser.parseDescriptor(context, resource, true);
+        return new ModuleDescriptorAdapter(moduleDescriptor.getModuleRevisionId(), moduleDescriptor);
+    }
+}
diff --git a/subprojects/core-impl/src/main/groovy/org/gradle/api/internal/artifacts/ivyservice/ivyresolve/parser/DescriptorParseContext.java b/subprojects/core-impl/src/main/groovy/org/gradle/api/internal/artifacts/ivyservice/ivyresolve/parser/DescriptorParseContext.java
new file mode 100644
index 0000000..475fc91
--- /dev/null
+++ b/subprojects/core-impl/src/main/groovy/org/gradle/api/internal/artifacts/ivyservice/ivyresolve/parser/DescriptorParseContext.java
@@ -0,0 +1,36 @@
+/*
+ * Copyright 2013 the original author or authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY 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.ivyresolve.parser;
+
+import org.apache.ivy.core.module.descriptor.Artifact;
+import org.apache.ivy.core.module.descriptor.ModuleDescriptor;
+import org.apache.ivy.core.module.id.ModuleRevisionId;
+import org.apache.ivy.plugins.matcher.PatternMatcher;
+
+public interface DescriptorParseContext {
+
+    String substitute(String value);
+
+    PatternMatcher getMatcher(String matcherName);
+
+    String getDefaultStatus();
+
+    ModuleDescriptor getModuleDescriptor(ModuleRevisionId mRevId);
+
+    boolean artifactExists(Artifact artifact);
+
+    ModuleRevisionId getCurrentRevisionId();
+}
diff --git a/subprojects/core-impl/src/main/groovy/org/gradle/api/internal/artifacts/ivyservice/ivyresolve/parser/DisconnectedDescriptorParseContext.java b/subprojects/core-impl/src/main/groovy/org/gradle/api/internal/artifacts/ivyservice/ivyresolve/parser/DisconnectedDescriptorParseContext.java
new file mode 100644
index 0000000..2c96c10
--- /dev/null
+++ b/subprojects/core-impl/src/main/groovy/org/gradle/api/internal/artifacts/ivyservice/ivyresolve/parser/DisconnectedDescriptorParseContext.java
@@ -0,0 +1,46 @@
+/*
+ * Copyright 2013 the original author or authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY 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.ivyresolve.parser;
+
+import org.apache.ivy.core.module.descriptor.Artifact;
+import org.apache.ivy.core.module.descriptor.DefaultModuleDescriptor;
+import org.apache.ivy.core.module.descriptor.ModuleDescriptor;
+import org.apache.ivy.core.module.id.ModuleRevisionId;
+
+import java.util.Date;
+
+/**
+ * An implementation of {@link DescriptorParseContext} that is useful for parsing an ivy.xml file without attempting to download
+ * other resources from a DependencyResolver.
+ */
+public class DisconnectedDescriptorParseContext extends AbstractDescriptorParseContext {
+    public DisconnectedDescriptorParseContext() {
+        super("integration");
+    }
+
+    public ModuleDescriptor getModuleDescriptor(ModuleRevisionId mRevId) {
+        return new DefaultModuleDescriptor(mRevId, "release", new Date());
+    }
+
+    public boolean artifactExists(Artifact artifact) {
+        return false;
+    }
+
+    public ModuleRevisionId getCurrentRevisionId() {
+        throw new UnsupportedOperationException();
+    }
+}
diff --git a/subprojects/core-impl/src/main/groovy/org/gradle/api/internal/artifacts/ivyservice/ivyresolve/parser/DisconnectedParserSettings.java b/subprojects/core-impl/src/main/groovy/org/gradle/api/internal/artifacts/ivyservice/ivyresolve/parser/DisconnectedParserSettings.java
deleted file mode 100644
index 67fc733..0000000
--- a/subprojects/core-impl/src/main/groovy/org/gradle/api/internal/artifacts/ivyservice/ivyresolve/parser/DisconnectedParserSettings.java
+++ /dev/null
@@ -1,110 +0,0 @@
-/*
- * Copyright 2013 the original author or authors.
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- *      http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY 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.ivyresolve.parser;
-
-import org.apache.ivy.core.RelativeUrlResolver;
-import org.apache.ivy.core.cache.ResolutionCacheManager;
-import org.apache.ivy.core.module.descriptor.DefaultModuleDescriptor;
-import org.apache.ivy.core.module.descriptor.DependencyDescriptor;
-import org.apache.ivy.core.module.id.ModuleId;
-import org.apache.ivy.core.module.id.ModuleRevisionId;
-import org.apache.ivy.core.module.status.StatusManager;
-import org.apache.ivy.core.resolve.ResolveData;
-import org.apache.ivy.core.resolve.ResolvedModuleRevision;
-import org.apache.ivy.core.settings.IvySettings;
-import org.apache.ivy.plugins.conflict.ConflictManager;
-import org.apache.ivy.plugins.matcher.PatternMatcher;
-import org.apache.ivy.plugins.namespace.Namespace;
-import org.apache.ivy.plugins.parser.ParserSettings;
-import org.apache.ivy.plugins.resolver.DependencyResolver;
-import org.gradle.api.internal.artifacts.ivyservice.ivyresolve.RestrictedDependencyResolver;
-
-import java.io.File;
-import java.text.ParseException;
-import java.util.Date;
-import java.util.Map;
-
-/**
- * An implementation of {@link ParserSettings} that is useful for parsing an ivy.xml file without attempting to download
- * other resources from a DependencyResolver.
- */
-public class DisconnectedParserSettings implements ParserSettings {
-    private final IvySettings ivySettings = new IvySettings();
-
-    /**
-     * This implementation will not attempt to download any parent modules.
-     * TODO:DAZ Work out how to do the actual download if we want to do more validation when publishing.
-     */
-    public DependencyResolver getResolver(final ModuleRevisionId mRevId) {
-        return new RestrictedDependencyResolver() {
-            @Override
-            public ResolvedModuleRevision getDependency(DependencyDescriptor dd, ResolveData data) throws ParseException {
-                return new ResolvedModuleRevision(null, null, new DefaultModuleDescriptor(mRevId, "release", new Date()), null);
-            }
-        };
-    }
-
-    public String substitute(String value) {
-        return ivySettings.substitute(value);
-    }
-
-    public PatternMatcher getMatcher(String matcherName) {
-        return ivySettings.getMatcher(matcherName);
-    }
-
-    public StatusManager getStatusManager() {
-        return ivySettings.getStatusManager();
-    }
-
-    public ConflictManager getConflictManager(String name) {
-        return ivySettings.getConflictManager(name);
-    }
-
-    public Namespace getNamespace(String namespace) {
-        return ivySettings.getNamespace(namespace);
-    }
-
-    public Namespace getContextNamespace() {
-        return ivySettings.getContextNamespace();
-    }
-
-    // The reset of the methods are not used when parsing an ivy.xml
-    public Map substitute(Map strings) {
-        throw unsupported();
-    }
-
-    public ResolutionCacheManager getResolutionCacheManager() {
-        throw unsupported();
-    }
-
-    public RelativeUrlResolver getRelativeUrlResolver() {
-        throw unsupported();
-    }
-
-    public File resolveFile(String filename) {
-        throw unsupported();
-    }
-
-    public String getDefaultBranch(ModuleId moduleId) {
-        throw unsupported();
-    }
-
-    private UnsupportedOperationException unsupported() {
-        return new UnsupportedOperationException();
-    }
-
-}
diff --git a/subprojects/core-impl/src/main/groovy/org/gradle/api/internal/artifacts/ivyservice/ivyresolve/parser/DownloadedIvyModuleDescriptorParser.java b/subprojects/core-impl/src/main/groovy/org/gradle/api/internal/artifacts/ivyservice/ivyresolve/parser/DownloadedIvyModuleDescriptorParser.java
index f1758d3..3c28fe4 100644
--- a/subprojects/core-impl/src/main/groovy/org/gradle/api/internal/artifacts/ivyservice/ivyresolve/parser/DownloadedIvyModuleDescriptorParser.java
+++ b/subprojects/core-impl/src/main/groovy/org/gradle/api/internal/artifacts/ivyservice/ivyresolve/parser/DownloadedIvyModuleDescriptorParser.java
@@ -17,17 +17,12 @@
 package org.gradle.api.internal.artifacts.ivyservice.ivyresolve.parser;
 
 import org.apache.ivy.core.module.descriptor.DefaultModuleDescriptor;
-import org.apache.ivy.plugins.parser.ParserSettings;
-import org.apache.ivy.plugins.repository.Resource;
-
-import java.io.IOException;
-import java.net.URL;
-import java.text.ParseException;
+import org.gradle.api.internal.externalresource.LocallyAvailableExternalResource;
 
 public class DownloadedIvyModuleDescriptorParser extends IvyXmlModuleDescriptorParser {
     @Override
-    public DefaultModuleDescriptor parseDescriptor(ParserSettings ivySettings, URL xmlURL, Resource res, boolean validate) throws ParseException, IOException {
-        DefaultModuleDescriptor descriptor = super.parseDescriptor(ivySettings, xmlURL, res, validate);
+    public DefaultModuleDescriptor parseDescriptor(DescriptorParseContext ivySettings, LocallyAvailableExternalResource resource, boolean validate) throws MetaDataParseException {
+        DefaultModuleDescriptor descriptor = super.parseDescriptor(ivySettings, resource, validate);
         descriptor.setDefault(false);
         return descriptor;
     }
diff --git a/subprojects/core-impl/src/main/groovy/org/gradle/api/internal/artifacts/ivyservice/ivyresolve/parser/GradlePomModuleDescriptorBuilder.java b/subprojects/core-impl/src/main/groovy/org/gradle/api/internal/artifacts/ivyservice/ivyresolve/parser/GradlePomModuleDescriptorBuilder.java
index f7b3517..66cf7cb 100644
--- a/subprojects/core-impl/src/main/groovy/org/gradle/api/internal/artifacts/ivyservice/ivyresolve/parser/GradlePomModuleDescriptorBuilder.java
+++ b/subprojects/core-impl/src/main/groovy/org/gradle/api/internal/artifacts/ivyservice/ivyresolve/parser/GradlePomModuleDescriptorBuilder.java
@@ -16,7 +16,6 @@
 package org.gradle.api.internal.artifacts.ivyservice.ivyresolve.parser;
 
 import org.apache.ivy.Ivy;
-import org.apache.ivy.core.cache.ArtifactOrigin;
 import org.apache.ivy.core.module.descriptor.*;
 import org.apache.ivy.core.module.descriptor.Configuration.Visibility;
 import org.apache.ivy.core.module.id.ArtifactId;
@@ -24,15 +23,13 @@ import org.apache.ivy.core.module.id.ModuleId;
 import org.apache.ivy.core.module.id.ModuleRevisionId;
 import org.apache.ivy.plugins.matcher.ExactPatternMatcher;
 import org.apache.ivy.plugins.matcher.PatternMatcher;
-import org.apache.ivy.plugins.parser.ModuleDescriptorParser;
-import org.apache.ivy.plugins.parser.ParserSettings;
 import org.apache.ivy.plugins.parser.m2.DefaultPomDependencyMgt;
 import org.apache.ivy.plugins.parser.m2.PomDependencyMgt;
-import org.apache.ivy.plugins.parser.m2.PomReader.PomDependencyData;
-import org.apache.ivy.plugins.repository.Resource;
-import org.apache.ivy.plugins.resolver.DependencyResolver;
-import org.apache.ivy.util.Message;
+import org.apache.ivy.plugins.parser.xml.XmlModuleDescriptorParser;
+import org.gradle.api.internal.externalresource.ExternalResource;
 import org.gradle.util.DeprecationLogger;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
 
 import java.util.*;
 import java.util.Map.Entry;
@@ -43,6 +40,7 @@ import java.util.Map.Entry;
  * classifiers)
  */
 public class GradlePomModuleDescriptorBuilder {
+    private static final Logger LOGGER = LoggerFactory.getLogger(GradlePomModuleDescriptorBuilder.class);
 
     private static final int DEPENDENCY_MANAGEMENT_KEY_PARTS_COUNT = 4;
 
@@ -164,15 +162,10 @@ public class GradlePomModuleDescriptorBuilder {
 
     private ModuleRevisionId mrid;
 
-    private ParserSettings parserSettings;
+    private DescriptorParseContext parserSettings;
 
-    private static final String WRONG_NUMBER_OF_PARTS_MSG = "what seemed to be a dependency "
-            + "management extra info exclusion had the wrong number of parts (should have 2) ";
-
-
-    public GradlePomModuleDescriptorBuilder(
-            ModuleDescriptorParser parser, Resource res, ParserSettings ivySettings) {
-        ivyModuleDescriptor = new DefaultModuleDescriptor(parser, res);
+    public GradlePomModuleDescriptorBuilder(ExternalResource res, DescriptorParseContext ivySettings) {
+        ivyModuleDescriptor = new DefaultModuleDescriptor(XmlModuleDescriptorParser.getInstance(), null);
         ivyModuleDescriptor.setResolvedPublicationDate(new Date(res.getLastModified()));
         for (Configuration maven2Configuration : MAVEN2_CONFIGURATIONS) {
             ivyModuleDescriptor.addConfiguration(maven2Configuration);
@@ -182,7 +175,7 @@ public class GradlePomModuleDescriptorBuilder {
         parserSettings = ivySettings;
     }
 
-    public ModuleDescriptor getModuleDescriptor() {
+    public DefaultModuleDescriptor getModuleDescriptor() {
         return ivyModuleDescriptor;
     }
 
@@ -213,34 +206,22 @@ public class GradlePomModuleDescriptorBuilder {
 
     public void addMainArtifact(String artifactId, String packaging) {
         if ("pom".equals(packaging)) {
-            // no artifact defined! Add the default artifact only if it exists
-            DependencyResolver resolver = parserSettings.getResolver(mrid);
-
-            if (resolver != null) {
-                DefaultArtifact artifact = new DefaultArtifact(mrid, new Date(), artifactId, "jar", "jar");
-
-                if (!ArtifactOrigin.isUnknown(resolver.locate(artifact))) {
-                    ivyModuleDescriptor.addArtifact("master", artifact);
-                }
+            DefaultArtifact artifact = new DefaultArtifact(mrid, new Date(), artifactId, "jar", "jar");
+            if (parserSettings.artifactExists(artifact)) {
+                ivyModuleDescriptor.addArtifact("master", artifact);
             }
 
             return;
         }
 
         if (!isKnownJarPackaging(packaging)) {
-            // Look for an artifact with extension = packaging. This is deprecated.
-            DependencyResolver resolver = parserSettings.getResolver(mrid);
-
-            if (resolver != null) {
-                DefaultArtifact artifact = new DefaultArtifact(mrid, new Date(), artifactId, packaging, packaging);
+            DefaultArtifact artifact = new DefaultArtifact(mrid, new Date(), artifactId, packaging, packaging);
 
-                if (!ArtifactOrigin.isUnknown(resolver.locate(artifact))) {
-                    ivyModuleDescriptor.addArtifact("master", artifact);
+            if (parserSettings.artifactExists(artifact)) {
+                ivyModuleDescriptor.addArtifact("master", artifact);
 
-                    DeprecationLogger.nagUserOfDeprecated("Relying on packaging to define the extension of the main artifact");
-
-                    return;
-                }
+                DeprecationLogger.nagUserOfDeprecated("Relying on packaging to define the extension of the main artifact");
+                return;
             }
         }
 
@@ -251,7 +232,7 @@ public class GradlePomModuleDescriptorBuilder {
         return "jar".equals(packaging) || JAR_PACKAGINGS.contains(packaging);
     }
 
-    public void addDependency(PomDependencyData dep) {
+    public void addDependency(PomReader.PomDependencyData dep) {
         String scope = dep.getScope();
         if ((scope != null) && (scope.length() > 0) && !MAVEN2_CONF_MAPPING.containsKey(scope)) {
             // unknown scope, defaulting to 'compile'
@@ -328,7 +309,7 @@ public class GradlePomModuleDescriptorBuilder {
     }
 
     public void addDependency(DependencyDescriptor descriptor) {
-        // Some POMs depend on theirselfves through their parent pom, don't add this dependency
+        // Some POMs depend on theirselfves through their parent POM, don't add this dependency
         // since Ivy doesn't allow this!
         // Example: http://repo2.maven.org/maven2/com/atomikos/atomikos-util/3.6.4/atomikos-util-3.6.4.pom
         ModuleId dependencyId = descriptor.getDependencyId();
@@ -424,12 +405,12 @@ public class GradlePomModuleDescriptorBuilder {
         }
     }
 
-    private String getDefaultVersion(PomDependencyData dep) {
+    private String getDefaultVersion(PomReader.PomDependencyData dep) {
         String key = getDependencyMgtExtraInfoKeyForVersion(dep.getGroupId(), dep.getArtifactId());
         return (String) ivyModuleDescriptor.getExtraInfo().get(key);
     }
 
-    private String getDefaultScope(PomDependencyData dep) {
+    private String getDefaultScope(PomReader.PomDependencyData dep) {
         String key = getDependencyMgtExtraInfoKeyForScope(dep.getGroupId(), dep.getArtifactId());
         String result = (String) ivyModuleDescriptor.getExtraInfo().get(key);
         if ((result == null) || !MAVEN2_CONF_MAPPING.containsKey(result)) {
@@ -469,8 +450,8 @@ public class GradlePomModuleDescriptorBuilder {
                 String fullExclusion = entry.getValue();
                 String[] exclusionParts = fullExclusion.split(EXTRA_INFO_DELIMITER);
                 if (exclusionParts.length != 2) {
-                    Message.error(WRONG_NUMBER_OF_PARTS_MSG + exclusionParts.length + " : "
-                            + fullExclusion);
+                    LOGGER.error("Wrong number of parts for dependency management extra info exclusion: expect 2, found {}: {}",
+                            exclusionParts.length, fullExclusion);
                     continue;
                 }
                 exclusionIds.add(ModuleId.newInstance(exclusionParts[0], exclusionParts[1]));
@@ -489,7 +470,7 @@ public class GradlePomModuleDescriptorBuilder {
             if (key.startsWith(DEPENDENCY_MANAGEMENT)) {
                 String[] parts = key.split(EXTRA_INFO_DELIMITER);
                 if (parts.length != DEPENDENCY_MANAGEMENT_KEY_PARTS_COUNT) {
-                    Message.warn("what seem to be a dependency management extra info doesn't match expected pattern: " + key);
+                    LOGGER.warn("Dependency management extra info doesn't match expected pattern: {}", key);
                 } else {
                     String versionKey = DEPENDENCY_MANAGEMENT + EXTRA_INFO_DELIMITER + parts[1]
                             + EXTRA_INFO_DELIMITER + parts[2]
diff --git a/subprojects/core-impl/src/main/groovy/org/gradle/api/internal/artifacts/ivyservice/ivyresolve/parser/GradlePomModuleDescriptorParser.java b/subprojects/core-impl/src/main/groovy/org/gradle/api/internal/artifacts/ivyservice/ivyresolve/parser/GradlePomModuleDescriptorParser.java
index e9209fd..f814e1b 100644
--- a/subprojects/core-impl/src/main/groovy/org/gradle/api/internal/artifacts/ivyservice/ivyresolve/parser/GradlePomModuleDescriptorParser.java
+++ b/subprojects/core-impl/src/main/groovy/org/gradle/api/internal/artifacts/ivyservice/ivyresolve/parser/GradlePomModuleDescriptorParser.java
@@ -15,36 +15,18 @@
  */
 package org.gradle.api.internal.artifacts.ivyservice.ivyresolve.parser;
 
-import org.apache.ivy.core.IvyContext;
 import org.apache.ivy.core.module.descriptor.*;
 import org.apache.ivy.core.module.descriptor.Configuration.Visibility;
 import org.apache.ivy.core.module.id.ModuleRevisionId;
-import org.apache.ivy.core.resolve.ResolveData;
-import org.apache.ivy.core.resolve.ResolveEngine;
-import org.apache.ivy.core.resolve.ResolveOptions;
-import org.apache.ivy.core.resolve.ResolvedModuleRevision;
-import org.apache.ivy.plugins.namespace.NameSpaceHelper;
-import org.apache.ivy.plugins.parser.ModuleDescriptorParser;
-import org.apache.ivy.plugins.parser.ParserSettings;
 import org.apache.ivy.plugins.parser.m2.PomDependencyMgt;
-import org.apache.ivy.plugins.parser.m2.PomReader;
-import org.apache.ivy.plugins.repository.Resource;
-import org.apache.ivy.plugins.repository.url.URLResource;
-import org.apache.ivy.plugins.resolver.DependencyResolver;
-import org.apache.ivy.util.Message;
-import org.gradle.internal.UncheckedException;
+import org.gradle.api.internal.externalresource.ExternalResource;
+import org.gradle.api.internal.externalresource.LocallyAvailableExternalResource;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
 import org.xml.sax.SAXException;
 
-import java.io.File;
 import java.io.IOException;
-import java.io.InputStream;
-import java.lang.reflect.InvocationHandler;
-import java.lang.reflect.InvocationTargetException;
-import java.lang.reflect.Method;
-import java.lang.reflect.Proxy;
-import java.net.URL;
 import java.text.ParseException;
-import java.util.Date;
 import java.util.List;
 import java.util.Map;
 
@@ -52,245 +34,177 @@ import java.util.Map;
  * This a straight copy of org.apache.ivy.plugins.parser.m2.PomModuleDescriptorParser, with one change: we do NOT attempt to retrieve source and javadoc artifacts when parsing the POM. This cuts the
  * number of remote call in half to resolve a module.
  */
-public final class GradlePomModuleDescriptorParser implements ModuleDescriptorParser {
-    public void toIvyFile(InputStream is, Resource res, File destFile, ModuleDescriptor md)
-            throws ParseException, IOException {
-        throw new UnsupportedOperationException();
-    }
+public final class GradlePomModuleDescriptorParser extends AbstractModuleDescriptorParser {
+    private static final Logger LOGGER = LoggerFactory.getLogger(GradlePomModuleDescriptorParser.class);
 
-    public boolean accept(Resource res) {
+    public boolean accept(ExternalResource res) {
         return res.getName().endsWith(".pom") || res.getName().endsWith("pom.xml")
                 || res.getName().endsWith("project.xml");
     }
 
-    public String toString() {
-        return "gradle pom parser";
-    }
-
-    public Artifact getMetadataArtifact(ModuleRevisionId mrid, Resource res) {
-        return DefaultArtifact.newPomArtifact(mrid, new Date(res.getLastModified()));
-    }
-
-    public String getType() {
-        return "pom";
+    @Override
+    protected String getTypeName() {
+        return "POM";
     }
 
-    public ModuleDescriptor parseDescriptor(ParserSettings ivySettings, URL descriptorURL,
-                                            boolean validate) throws ParseException, IOException {
-        URLResource resource = new URLResource(descriptorURL);
-        return parseDescriptor(ivySettings, descriptorURL, resource, validate);
+    public String toString() {
+        return "gradle pom parser";
     }
 
-    public ModuleDescriptor parseDescriptor(ParserSettings ivySettings, URL descriptorURL,
-                                            Resource resource, boolean validate) throws ParseException, IOException {
+    protected DefaultModuleDescriptor doParseDescriptor(DescriptorParseContext parserSettings, LocallyAvailableExternalResource resource, boolean validate) throws IOException, ParseException, SAXException {
+        GradlePomModuleDescriptorBuilder mdBuilder = new GradlePomModuleDescriptorBuilder(resource, parserSettings);
 
-        Resource res = encodedUrlResource(resource, descriptorURL);
-        GradlePomModuleDescriptorBuilder mdBuilder = new GradlePomModuleDescriptorBuilder(this, res, ivySettings);
+        PomReader pomReader = new PomReader(resource);
+        pomReader.setProperty("parent.version", pomReader.getParentVersion());
+        pomReader.setProperty("parent.groupId", pomReader.getParentGroupId());
+        pomReader.setProperty("project.parent.version", pomReader.getParentVersion());
+        pomReader.setProperty("project.parent.groupId", pomReader.getParentGroupId());
 
-        try {
-            PomReader domReader = new PomReader(descriptorURL, res);
-            domReader.setProperty("parent.version", domReader.getParentVersion());
-            domReader.setProperty("parent.groupId", domReader.getParentGroupId());
-            domReader.setProperty("project.parent.version", domReader.getParentVersion());
-            domReader.setProperty("project.parent.groupId", domReader.getParentGroupId());
+        Map pomProperties = pomReader.getPomProperties();
+        for (Object o : pomProperties.entrySet()) {
+            Map.Entry prop = (Map.Entry) o;
+            pomReader.setProperty((String) prop.getKey(), (String) prop.getValue());
+            mdBuilder.addProperty((String) prop.getKey(), (String) prop.getValue());
+        }
 
-            Map pomProperties = domReader.getPomProperties();
-            for (Object o : pomProperties.entrySet()) {
-                Map.Entry prop = (Map.Entry) o;
-                domReader.setProperty((String) prop.getKey(), (String) prop.getValue());
-                mdBuilder.addProperty((String) prop.getKey(), (String) prop.getValue());
+        ModuleDescriptor parentDescr = null;
+        if (pomReader.hasParent()) {
+            //Is there any other parent properties?
+
+            ModuleRevisionId parentModRevID = ModuleRevisionId.newInstance(
+                    pomReader.getParentGroupId(),
+                    pomReader.getParentArtifactId(),
+                    pomReader.getParentVersion());
+            parentDescr = parseOtherPom(parserSettings, parentModRevID);
+            if (parentDescr == null) {
+                throw new IOException("Impossible to load parent for " + resource.getName() + ". Parent=" + parentModRevID);
             }
-
-            ModuleDescriptor parentDescr = null;
-            if (domReader.hasParent()) {
-                //Is there any other parent properties?
-
-                ModuleRevisionId parentModRevID = ModuleRevisionId.newInstance(
-                        domReader.getParentGroupId(),
-                        domReader.getParentArtifactId(),
-                        domReader.getParentVersion());
-                ResolvedModuleRevision parentModule = parseOtherPom(ivySettings, parentModRevID);
-                if (parentModule != null) {
-                    parentDescr = parentModule.getDescriptor();
-                } else {
-                    throw new IOException("Impossible to load parent for " + res.getName() + ". Parent=" + parentModRevID);
-                }
-                if (parentDescr != null) {
-                    Map parentPomProps = GradlePomModuleDescriptorBuilder.extractPomProperties(parentDescr.getExtraInfo());
-                    for (Object o : parentPomProps.entrySet()) {
-                        Map.Entry prop = (Map.Entry) o;
-                        domReader.setProperty((String) prop.getKey(), (String) prop.getValue());
-                    }
-                }
+            Map parentPomProps = GradlePomModuleDescriptorBuilder.extractPomProperties(parentDescr.getExtraInfo());
+            for (Object o : parentPomProps.entrySet()) {
+                Map.Entry prop = (Map.Entry) o;
+                pomReader.setProperty((String) prop.getKey(), (String) prop.getValue());
             }
+        }
 
-            String groupId = domReader.getGroupId();
-            String artifactId = domReader.getArtifactId();
-            String version = domReader.getVersion();
-            ModuleScopedParserSettings scopedSettings = (ModuleScopedParserSettings) ivySettings;
-            mdBuilder.setModuleRevId(scopedSettings.getCurrentRevisionId(), groupId, artifactId, version);
-
-            mdBuilder.setHomePage(domReader.getHomePage());
-            mdBuilder.setDescription(domReader.getDescription());
-            mdBuilder.setLicenses(domReader.getLicenses());
-
-            ModuleRevisionId relocation = domReader.getRelocation();
-
-            if (relocation != null) {
-                if (groupId != null && artifactId != null
-                        && artifactId.equals(relocation.getName())
-                        && groupId.equals(relocation.getOrganisation())) {
-                    Message.error("Relocation to an other version number not supported in ivy : "
+        String groupId = pomReader.getGroupId();
+        String artifactId = pomReader.getArtifactId();
+        String version = pomReader.getVersion();
+        mdBuilder.setModuleRevId(parserSettings.getCurrentRevisionId(), groupId, artifactId, version);
+
+        mdBuilder.setHomePage(pomReader.getHomePage());
+        mdBuilder.setDescription(pomReader.getDescription());
+        mdBuilder.setLicenses(pomReader.getLicenses());
+
+        ModuleRevisionId relocation = pomReader.getRelocation();
+
+        if (relocation != null) {
+            if (groupId != null && artifactId != null
+                    && artifactId.equals(relocation.getName())
+                    && groupId.equals(relocation.getOrganisation())) {
+                LOGGER.error("POM relocation to an other version number is not fully supported in Gradle : {} relocated to {}.",
+                        mdBuilder.getModuleDescriptor().getModuleRevisionId(), relocation);
+                LOGGER.warn("Please update your dependency to directly use the correct version '{}'.", relocation);
+                LOGGER.warn("Resolution will only pick dependencies of the relocated element.  Artifacts and other metadata will be ignored.");
+                ModuleDescriptor relocatedModule = parseOtherPom(parserSettings, relocation);
+                if (relocatedModule == null) {
+                    throw new ParseException("impossible to load module "
+                            + relocation + " to which "
                             + mdBuilder.getModuleDescriptor().getModuleRevisionId()
-                            + " relocated to " + relocation
-                            + ". Please update your dependency to directly use the right version.");
-                    Message.warn("Resolution will only pick dependencies of the relocated element."
-                            + "  Artefact and other metadata will be ignored.");
-                    ResolvedModuleRevision relocatedModule = parseOtherPom(ivySettings, relocation);
-                    if (relocatedModule == null) {
-                        throw new ParseException("impossible to load module "
-                                + relocation + " to which "
-                                + mdBuilder.getModuleDescriptor().getModuleRevisionId()
-                                + " has been relocated", 0);
-                    }
-                    DependencyDescriptor[] dds = relocatedModule.getDescriptor().getDependencies();
-                    for (DependencyDescriptor dd : dds) {
-                        mdBuilder.addDependency(dd);
-                    }
-                } else {
-                    Message.info(mdBuilder.getModuleDescriptor().getModuleRevisionId()
-                            + " is relocated to " + relocation
-                            + ". Please update your dependencies.");
-                    Message.verbose("Relocated module will be considered as a dependency");
-                    DefaultDependencyDescriptor dd = new DefaultDependencyDescriptor(mdBuilder.getModuleDescriptor(), relocation, true, false, true);
-                    /* Map all public dependencies */
-                    Configuration[] m2Confs = GradlePomModuleDescriptorBuilder.MAVEN2_CONFIGURATIONS;
-                    for (Configuration m2Conf : m2Confs) {
-                        if (Visibility.PUBLIC.equals(m2Conf.getVisibility())) {
-                            dd.addDependencyConfiguration(m2Conf.getName(), m2Conf.getName());
-                        }
-                    }
+                            + " has been relocated", 0);
+                }
+                DependencyDescriptor[] dds = relocatedModule.getDependencies();
+                for (DependencyDescriptor dd : dds) {
                     mdBuilder.addDependency(dd);
                 }
             } else {
-                domReader.setProperty("project.groupId", groupId);
-                domReader.setProperty("pom.groupId", groupId);
-                domReader.setProperty("groupId", groupId);
-                domReader.setProperty("project.artifactId", artifactId);
-                domReader.setProperty("pom.artifactId", artifactId);
-                domReader.setProperty("artifactId", artifactId);
-                domReader.setProperty("project.version", version);
-                domReader.setProperty("pom.version", version);
-                domReader.setProperty("version", version);
-
-                if (parentDescr != null) {
-                    mdBuilder.addExtraInfos(parentDescr.getExtraInfo());
-
-                    // add dependency management info from parent
-                    List depMgt = GradlePomModuleDescriptorBuilder.getDependencyManagements(parentDescr);
-                    for (Object aDepMgt : depMgt) {
-                        mdBuilder.addDependencyMgt((PomDependencyMgt) aDepMgt);
-                    }
-
-                    // add plugins from parent
-                    List /*<PomDependencyMgt>*/ plugins = GradlePomModuleDescriptorBuilder.getPlugins(parentDescr);
-                    for (Object plugin : plugins) {
-                        mdBuilder.addPlugin((PomDependencyMgt) plugin);
+                LOGGER.info(mdBuilder.getModuleDescriptor().getModuleRevisionId()
+                        + " is relocated to " + relocation
+                        + ". Please update your dependencies.");
+                LOGGER.debug("Relocated module will be considered as a dependency");
+                DefaultDependencyDescriptor dd = new DefaultDependencyDescriptor(mdBuilder.getModuleDescriptor(), relocation, true, false, true);
+                /* Map all public dependencies */
+                Configuration[] m2Confs = GradlePomModuleDescriptorBuilder.MAVEN2_CONFIGURATIONS;
+                for (Configuration m2Conf : m2Confs) {
+                    if (Visibility.PUBLIC.equals(m2Conf.getVisibility())) {
+                        dd.addDependencyConfiguration(m2Conf.getName(), m2Conf.getName());
                     }
                 }
-
-                for (Object o : domReader.getDependencyMgt()) {
-                    PomDependencyMgt dep = (PomDependencyMgt) o;
-                    if ("import".equals(dep.getScope())) {
-                        ModuleRevisionId importModRevID = ModuleRevisionId.newInstance(
-                                dep.getGroupId(),
-                                dep.getArtifactId(),
-                                dep.getVersion());
-                        ResolvedModuleRevision importModule = parseOtherPom(ivySettings, importModRevID);
-                        if (importModule != null) {
-                            ModuleDescriptor importDescr = importModule.getDescriptor();
-
-                            // add dependency management info from imported module
-                            List depMgt = GradlePomModuleDescriptorBuilder.getDependencyManagements(importDescr);
-                            for (Object aDepMgt : depMgt) {
-                                mdBuilder.addDependencyMgt((PomDependencyMgt) aDepMgt);
-                            }
-                        } else {
-                            throw new IOException("Impossible to import module for " + res.getName() + "."
-                                    + " Import=" + importModRevID);
-                        }
-
-                    } else {
-                        mdBuilder.addDependencyMgt(dep);
-                    }
+                mdBuilder.addDependency(dd);
+            }
+        } else {
+            pomReader.setProperty("project.groupId", groupId);
+            pomReader.setProperty("pom.groupId", groupId);
+            pomReader.setProperty("groupId", groupId);
+            pomReader.setProperty("project.artifactId", artifactId);
+            pomReader.setProperty("pom.artifactId", artifactId);
+            pomReader.setProperty("artifactId", artifactId);
+            pomReader.setProperty("project.version", version);
+            pomReader.setProperty("pom.version", version);
+            pomReader.setProperty("version", version);
+
+            if (parentDescr != null) {
+                mdBuilder.addExtraInfos(parentDescr.getExtraInfo());
+
+                // add dependency management info from parent
+                List depMgt = GradlePomModuleDescriptorBuilder.getDependencyManagements(parentDescr);
+                for (Object aDepMgt : depMgt) {
+                    mdBuilder.addDependencyMgt((PomDependencyMgt) aDepMgt);
                 }
 
-                for (Object o : domReader.getDependencies()) {
-                    PomReader.PomDependencyData dep = (PomReader.PomDependencyData) o;
-                    mdBuilder.addDependency(dep);
+                // add plugins from parent
+                List /*<PomDependencyMgt>*/ plugins = GradlePomModuleDescriptorBuilder.getPlugins(parentDescr);
+                for (Object plugin : plugins) {
+                    mdBuilder.addPlugin((PomDependencyMgt) plugin);
                 }
+            }
 
-                if (parentDescr != null) {
-                    for (int i = 0; i < parentDescr.getDependencies().length; i++) {
-                        mdBuilder.addDependency(parentDescr.getDependencies()[i]);
+            for (Object o : pomReader.getDependencyMgt()) {
+                PomDependencyMgt dep = (PomDependencyMgt) o;
+                if ("import".equals(dep.getScope())) {
+                    ModuleRevisionId importModRevID = ModuleRevisionId.newInstance(
+                            dep.getGroupId(),
+                            dep.getArtifactId(),
+                            dep.getVersion());
+                    ModuleDescriptor importDescr = parseOtherPom(parserSettings, importModRevID);
+                    if (importDescr == null) {
+                        throw new IOException("Impossible to import module for " + resource.getName() + "."
+                                + " Import=" + importModRevID);
+                    }
+                    // add dependency management info from imported module
+                    List depMgt = GradlePomModuleDescriptorBuilder.getDependencyManagements(importDescr);
+                    for (Object aDepMgt : depMgt) {
+                        mdBuilder.addDependencyMgt((PomDependencyMgt) aDepMgt);
                     }
-                }
 
-                for (Object o : domReader.getPlugins()) {
-                    PomReader.PomPluginElement plugin = (PomReader.PomPluginElement) o;
-                    mdBuilder.addPlugin(plugin);
+                } else {
+                    mdBuilder.addDependencyMgt(dep);
                 }
+            }
 
-                mdBuilder.addMainArtifact(artifactId, domReader.getPackaging());
+            for (Object o : pomReader.getDependencies()) {
+                PomReader.PomDependencyData dep = (PomReader.PomDependencyData) o;
+                mdBuilder.addDependency(dep);
             }
-        } catch (SAXException e) {
-            throw newParserException(e);
-        }
 
-        return mdBuilder.getModuleDescriptor();
-    }
+            if (parentDescr != null) {
+                for (int i = 0; i < parentDescr.getDependencies().length; i++) {
+                    mdBuilder.addDependency(parentDescr.getDependencies()[i]);
+                }
+            }
 
-    private ResolvedModuleRevision parseOtherPom(ParserSettings ivySettings,
-                                                 ModuleRevisionId parentModRevID) throws ParseException {
-        DependencyDescriptor dd = new DefaultDependencyDescriptor(parentModRevID, true);
-        ResolveData data = IvyContext.getContext().getResolveData();
-        if (data == null) {
-            ResolveEngine engine = IvyContext.getContext().getIvy().getResolveEngine();
-            ResolveOptions options = new ResolveOptions();
-            options.setDownload(false);
-            data = new ResolveData(engine, options);
-        }
+            for (Object o : pomReader.getPlugins()) {
+                PomReader.PomPluginElement plugin = (PomReader.PomPluginElement) o;
+                mdBuilder.addPlugin(plugin);
+            }
 
-        DependencyResolver resolver = ivySettings.getResolver(parentModRevID);
-        if (resolver == null) {
-            // TODO: Throw exception here?
-            return null;
-        } else {
-            dd = NameSpaceHelper.toSystem(dd, ivySettings.getContextNamespace());
-            return resolver.getDependency(dd, data);
+            mdBuilder.addMainArtifact(artifactId, pomReader.getPackaging());
         }
-    }
 
-    private ParseException newParserException(Exception e) {
-        Message.error(e.getMessage());
-        ParseException pe = new ParseException(e.getMessage(), 0);
-        pe.initCause(e);
-        return pe;
+        return mdBuilder.getModuleDescriptor();
     }
 
-    private Resource encodedUrlResource(final Resource base, final URL url) {
-        Object proxy = Proxy.newProxyInstance(getClass().getClassLoader(), new Class[]{Resource.class}, new InvocationHandler() {
-            public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
-                if ("getName".equals(method.getName())) {
-                    return url.toString();
-                }
-                try {
-                    return method.invoke(base, args);
-                } catch (InvocationTargetException e) {
-                    throw UncheckedException.throwAsUncheckedException(e.getTargetException());
-                }
-            }
-        });
-        return (Resource) proxy;
+    private ModuleDescriptor parseOtherPom(DescriptorParseContext ivySettings,
+                                           ModuleRevisionId parentModRevID) throws ParseException {
+        return ivySettings.getModuleDescriptor(parentModRevID);
     }
 }
diff --git a/subprojects/core-impl/src/main/groovy/org/gradle/api/internal/artifacts/ivyservice/ivyresolve/parser/IvyXmlModuleDescriptorParser.java b/subprojects/core-impl/src/main/groovy/org/gradle/api/internal/artifacts/ivyservice/ivyresolve/parser/IvyXmlModuleDescriptorParser.java
index 4476247..45135bf 100644
--- a/subprojects/core-impl/src/main/groovy/org/gradle/api/internal/artifacts/ivyservice/ivyresolve/parser/IvyXmlModuleDescriptorParser.java
+++ b/subprojects/core-impl/src/main/groovy/org/gradle/api/internal/artifacts/ivyservice/ivyresolve/parser/IvyXmlModuleDescriptorParser.java
@@ -16,51 +16,43 @@
 
 package org.gradle.api.internal.artifacts.ivyservice.ivyresolve.parser;
 
-import org.apache.ivy.core.IvyContext;
+import com.google.common.base.Joiner;
 import org.apache.ivy.core.NormalRelativeUrlResolver;
 import org.apache.ivy.core.RelativeUrlResolver;
 import org.apache.ivy.core.module.descriptor.*;
 import org.apache.ivy.core.module.id.ArtifactId;
 import org.apache.ivy.core.module.id.ModuleId;
 import org.apache.ivy.core.module.id.ModuleRevisionId;
-import org.apache.ivy.core.resolve.ResolveData;
-import org.apache.ivy.core.resolve.ResolveEngine;
-import org.apache.ivy.core.resolve.ResolveOptions;
-import org.apache.ivy.core.resolve.ResolvedModuleRevision;
 import org.apache.ivy.plugins.matcher.PatternMatcher;
-import org.apache.ivy.plugins.namespace.NameSpaceHelper;
 import org.apache.ivy.plugins.namespace.Namespace;
-import org.apache.ivy.plugins.parser.AbstractModuleDescriptorParser;
-import org.apache.ivy.plugins.parser.ModuleDescriptorParser;
-import org.apache.ivy.plugins.parser.ParserSettings;
-import org.apache.ivy.plugins.repository.Resource;
-import org.apache.ivy.plugins.repository.url.URLResource;
-import org.apache.ivy.plugins.resolver.DependencyResolver;
+import org.apache.ivy.plugins.parser.xml.XmlModuleDescriptorParser;
 import org.apache.ivy.util.XMLHelper;
 import org.apache.ivy.util.extendable.DefaultExtendableItem;
-import org.apache.ivy.util.extendable.ExtendableItemHelper;
+import org.gradle.api.Action;
+import org.gradle.api.internal.externalresource.ExternalResource;
+import org.gradle.api.internal.externalresource.LocallyAvailableExternalResource;
+import org.gradle.api.internal.externalresource.UrlExternalResource;
 import org.gradle.util.DeprecationLogger;
+import org.gradle.util.TextUtil;
 import org.slf4j.Logger;
 import org.slf4j.LoggerFactory;
 import org.xml.sax.Attributes;
+import org.xml.sax.InputSource;
 import org.xml.sax.SAXException;
+import org.xml.sax.SAXParseException;
+import org.xml.sax.helpers.DefaultHandler;
 
 import javax.xml.parsers.ParserConfigurationException;
-import java.io.File;
 import java.io.IOException;
 import java.io.InputStream;
 import java.net.MalformedURLException;
 import java.net.URL;
 import java.text.ParseException;
 import java.text.SimpleDateFormat;
-import java.util.Arrays;
-import java.util.LinkedHashMap;
-import java.util.List;
-import java.util.Map;
+import java.util.*;
 
 /**
- * Copied from org.apache.ivy.plugins.parser.xml.XmlModuleDescriptorParser into gradle codebase to make
- * it thread-safe.
+ * Copied from org.apache.ivy.plugins.parser.xml.XmlModuleDescriptorParser into Gradle codebase.
  */
 public class IvyXmlModuleDescriptorParser extends AbstractModuleDescriptorParser {
     static final String[] DEPENDENCY_REGULAR_ATTRIBUTES =
@@ -70,25 +62,315 @@ public class IvyXmlModuleDescriptorParser extends AbstractModuleDescriptorParser
 
     private static final Logger LOGGER = LoggerFactory.getLogger(IvyXmlModuleDescriptorParser.class);
 
-    public DefaultModuleDescriptor parseDescriptor(ParserSettings ivySettings, URL xmlURL, Resource res, boolean validate) throws ParseException, IOException {
-        Parser parser = new Parser(this, ivySettings, res);
+    protected DefaultModuleDescriptor doParseDescriptor(DescriptorParseContext ivySettings, LocallyAvailableExternalResource resource, boolean validate) throws IOException, ParseException {
+        Parser parser = new Parser(this, ivySettings, resource, resource.getLocalResource().getFile().toURI().toURL());
         parser.setValidate(validate);
-        parser.setInput(xmlURL);
         parser.parse();
-        return (DefaultModuleDescriptor) parser.getModuleDescriptor();
+        return parser.getModuleDescriptor();
     }
 
-    public ModuleDescriptor parseDescriptor(ParserSettings ivySettings, URL descriptorURL, boolean validate) throws ParseException, IOException {
-        return parseDescriptor(ivySettings, descriptorURL, new URLResource(descriptorURL), validate);
+    @Override
+    protected String getTypeName() {
+        return "Ivy file";
     }
 
-    public boolean accept(Resource res) {
+    public boolean accept(ExternalResource res) {
         return true; // this the default parser, it thus accepts all resources
     }
 
-    public void toIvyFile(InputStream is, Resource res, File destFile, ModuleDescriptor md)
-            throws IOException, ParseException {
-        throw new UnsupportedOperationException();
+    protected abstract static class AbstractParser extends DefaultHandler {
+        private static final String DEFAULT_CONF_MAPPING = "*->*";
+
+        private String defaultConf; // used only as defaultconf, not used for
+
+        // guessing right side part of a mapping
+        private String defaultConfMapping; // same as default conf but is used
+
+        // for guessing right side part of a mapping
+        private DefaultDependencyDescriptor defaultConfMappingDescriptor;
+
+        private final ExternalResource res;
+
+        private final List<String> errors = new ArrayList<String>();
+
+        private final DefaultModuleDescriptor md;
+
+        private final IvyXmlModuleDescriptorParser parser;
+
+        protected AbstractParser(IvyXmlModuleDescriptorParser parser, ExternalResource resource) {
+            this.parser = parser;
+            this.res = resource; // used for log and date only
+            md = new DefaultModuleDescriptor(XmlModuleDescriptorParser.getInstance(), null);
+            md.setLastModified(res.getLastModified());
+        }
+
+        public IvyXmlModuleDescriptorParser getModuleDescriptorParser() {
+            return parser;
+        }
+
+        protected void checkErrors() throws ParseException {
+            if (!errors.isEmpty()) {
+                throw new ParseException(Joiner.on(TextUtil.getPlatformLineSeparator()).join(errors), 0);
+            }
+        }
+
+        protected ExternalResource getResource() {
+            return res;
+        }
+
+        protected String getDefaultConfMapping() {
+            return defaultConfMapping;
+        }
+
+        protected void setDefaultConfMapping(String defaultConf) {
+            defaultConfMapping = defaultConf;
+        }
+
+        protected void parseDepsConfs(String confs, DefaultDependencyDescriptor dd) {
+            parseDepsConfs(confs, dd, defaultConfMapping != null);
+        }
+
+        protected void parseDepsConfs(String confs, DefaultDependencyDescriptor dd,
+                boolean useDefaultMappingToGuessRightOperande) {
+            parseDepsConfs(confs, dd, useDefaultMappingToGuessRightOperande, true);
+        }
+
+        protected void parseDepsConfs(String confs, DefaultDependencyDescriptor dd,
+                boolean useDefaultMappingToGuessRightOperande, boolean evaluateConditions) {
+            if (confs == null) {
+                return;
+            }
+
+            String[] conf = confs.split(";");
+            parseDepsConfs(conf, dd, useDefaultMappingToGuessRightOperande, evaluateConditions);
+        }
+
+        protected void parseDepsConfs(String[] conf, DefaultDependencyDescriptor dd,
+                boolean useDefaultMappingToGuessRightOperande) {
+            parseDepsConfs(conf, dd, useDefaultMappingToGuessRightOperande, true);
+        }
+
+        protected void parseDepsConfs(String[] conf, DefaultDependencyDescriptor dd,
+                boolean useDefaultMappingToGuessRightOperande, boolean evaluateConditions) {
+            replaceConfigurationWildcards(md);
+            for (int i = 0; i < conf.length; i++) {
+                String[] ops = conf[i].split("->");
+                if (ops.length == 1) {
+                    String[] modConfs = ops[0].split(",");
+                    if (!useDefaultMappingToGuessRightOperande) {
+                        for (int j = 0; j < modConfs.length; j++) {
+                            dd.addDependencyConfiguration(modConfs[j].trim(), modConfs[j].trim());
+                        }
+                    } else {
+                        for (int j = 0; j < modConfs.length; j++) {
+                            String[] depConfs = getDefaultConfMappingDescriptor()
+                                    .getDependencyConfigurations(modConfs[j]);
+                            if (depConfs.length > 0) {
+                                for (int k = 0; k < depConfs.length; k++) {
+                                    String mappedDependency = evaluateConditions
+                                    ? evaluateCondition(depConfs[k].trim(), dd)
+                                            : depConfs[k].trim();
+                                    if (mappedDependency != null) {
+                                        dd.addDependencyConfiguration(modConfs[j].trim(),
+                                            mappedDependency);
+                                    }
+                                }
+                            } else {
+                                // no default mapping found for this configuration, map
+                                // configuration to itself
+                                dd.addDependencyConfiguration(modConfs[j].trim(), modConfs[j]
+                                        .trim());
+                            }
+                        }
+                    }
+                } else if (ops.length == 2) {
+                    String[] modConfs = ops[0].split(",");
+                    String[] depConfs = ops[1].split(",");
+                    for (int j = 0; j < modConfs.length; j++) {
+                        for (int k = 0; k < depConfs.length; k++) {
+                            String mappedDependency = evaluateConditions ? evaluateCondition(
+                                depConfs[k].trim(), dd) : depConfs[k].trim();
+                            if (mappedDependency != null) {
+                                dd.addDependencyConfiguration(modConfs[j].trim(), mappedDependency);
+                            }
+                        }
+                    }
+                } else {
+                    addError("invalid conf " + conf[i] + " for " + dd);
+                }
+            }
+
+            if (md.isMappingOverride()) {
+                addExtendingConfigurations(conf, dd, useDefaultMappingToGuessRightOperande);
+            }
+        }
+
+        /**
+         * Evaluate the optional condition in the given configuration, like "[org=MYORG]confX". If
+         * the condition evaluates to true, the configuration is returned, if the condition
+         * evaluatate to false, null is returned. If there are no conditions, the configuration
+         * itself is returned.
+         *
+         * @param conf
+         *            the configuration to evaluate
+         * @param dd
+         *            the dependencydescriptor to which the configuration will be added
+         * @return the evaluated condition
+         */
+        private String evaluateCondition(String conf, DefaultDependencyDescriptor dd) {
+            if (conf.charAt(0) != '[') {
+                return conf;
+            }
+
+            int endConditionIndex = conf.indexOf(']');
+            if (endConditionIndex == -1) {
+                addError("invalid conf " + conf + " for " + dd);
+                return null;
+            }
+
+            String condition = conf.substring(1, endConditionIndex);
+
+            int notEqualIndex = condition.indexOf("!=");
+            if (notEqualIndex == -1) {
+                int equalIndex = condition.indexOf('=');
+                if (equalIndex == -1) {
+                    addError("invalid conf " + conf + " for " + dd.getDependencyRevisionId());
+                    return null;
+                }
+
+                String leftOp = condition.substring(0, equalIndex).trim();
+                String rightOp = condition.substring(equalIndex + 1).trim();
+
+                // allow organisation synonyms, like 'org' or 'organization'
+                if (leftOp.equals("org") || leftOp.equals("organization")) {
+                    leftOp = "organisation";
+                }
+
+                String attrValue = dd.getAttribute(leftOp);
+                if (!rightOp.equals(attrValue)) {
+                    return null;
+                }
+            } else {
+                String leftOp = condition.substring(0, notEqualIndex).trim();
+                String rightOp = condition.substring(notEqualIndex + 2).trim();
+
+                // allow organisation synonyms, like 'org' or 'organization'
+                if (leftOp.equals("org") || leftOp.equals("organization")) {
+                    leftOp = "organisation";
+                }
+
+                String attrValue = dd.getAttribute(leftOp);
+                if (rightOp.equals(attrValue)) {
+                    return null;
+                }
+            }
+
+            return conf.substring(endConditionIndex + 1);
+        }
+
+        private void addExtendingConfigurations(String[] confs, DefaultDependencyDescriptor dd,
+                boolean useDefaultMappingToGuessRightOperande) {
+            for (int i = 0; i < confs.length; i++) {
+                addExtendingConfigurations(confs[i], dd, useDefaultMappingToGuessRightOperande);
+            }
+        }
+
+        private void addExtendingConfigurations(String conf, DefaultDependencyDescriptor dd,
+                boolean useDefaultMappingToGuessRightOperande) {
+            Set configsToAdd = new HashSet();
+            Configuration[] configs = md.getConfigurations();
+            for (int i = 0; i < configs.length; i++) {
+                String[] ext = configs[i].getExtends();
+                for (int j = 0; j < ext.length; j++) {
+                    if (conf.equals(ext[j])) {
+                        String configName = configs[i].getName();
+                        configsToAdd.add(configName);
+                        addExtendingConfigurations(configName, dd,
+                            useDefaultMappingToGuessRightOperande);
+                    }
+                }
+            }
+
+            String[] confs = (String[]) configsToAdd.toArray(new String[configsToAdd.size()]);
+            parseDepsConfs(confs, dd, useDefaultMappingToGuessRightOperande);
+        }
+
+        protected DependencyDescriptor getDefaultConfMappingDescriptor() {
+            if (defaultConfMappingDescriptor == null) {
+                defaultConfMappingDescriptor = new DefaultDependencyDescriptor(ModuleRevisionId
+                        .newInstance("", "", ""), false);
+                parseDepsConfs(defaultConfMapping, defaultConfMappingDescriptor, false, false);
+            }
+            return defaultConfMappingDescriptor;
+        }
+
+        protected void addError(String msg) {
+            errors.add(msg + " in " + res.getName());
+        }
+
+        public void warning(SAXParseException ex) {
+            LOGGER.warn("xml parsing: " + getLocationString(ex) + ": " + ex.getMessage());
+        }
+
+        public void error(SAXParseException ex) {
+            addError("xml parsing: " + getLocationString(ex) + ": " + ex.getMessage());
+        }
+
+        public void fatalError(SAXParseException ex) throws SAXException {
+            addError("[Fatal Error] " + getLocationString(ex) + ": " + ex.getMessage());
+        }
+
+        /** Returns a string of the location. */
+        private String getLocationString(SAXParseException ex) {
+            StringBuffer str = new StringBuffer();
+
+            String systemId = ex.getSystemId();
+            if (systemId != null) {
+                int index = systemId.lastIndexOf('/');
+                if (index != -1) {
+                    systemId = systemId.substring(index + 1);
+                }
+                str.append(systemId);
+            } else {
+                str.append(getResource().getName());
+            }
+            str.append(':');
+            str.append(ex.getLineNumber());
+            str.append(':');
+            str.append(ex.getColumnNumber());
+
+            return str.toString();
+
+        } // getLocationString(SAXParseException):String
+
+        protected String getDefaultConf() {
+            return defaultConf != null ? defaultConf
+                    : (defaultConfMapping != null ? defaultConfMapping : DEFAULT_CONF_MAPPING);
+        }
+
+        protected void setDefaultConf(String defaultConf) {
+            this.defaultConf = defaultConf;
+        }
+
+        public DefaultModuleDescriptor getModuleDescriptor() throws ParseException {
+            checkErrors();
+            return md;
+        }
+
+        protected Date getDefaultPubDate() {
+            return new Date(md.getLastModified());
+        }
+
+        private void replaceConfigurationWildcards(ModuleDescriptor md) {
+            Configuration[] configs = md.getConfigurations();
+            for (int i = 0; i < configs.length; i++) {
+                configs[i].replaceWildcards(md);
+            }
+        }
+
+        protected DefaultModuleDescriptor getMd() {
+            return md;
+        }
     }
 
     public static class Parser extends AbstractParser {
@@ -126,12 +408,10 @@ public class IvyXmlModuleDescriptorParser extends AbstractModuleDescriptorParser
         private static final List ALLOWED_VERSIONS = Arrays.asList("1.0", "1.1", "1.2", "1.3", "1.4", "2.0", "2.1", "2.2");
 
         /* how and what do we have to parse */
-        private ParserSettings parserSettings;
+        private final DescriptorParseContext parserSettings;
         private final RelativeUrlResolver relativeUrlResolver = new NormalRelativeUrlResolver();
+        private final URL descriptorURL;
         private boolean validate = true;
-        private URL descriptorURL;
-        private InputStream descriptorInput;
-
 
         /* Parsing state */
         private int state = State.NONE;
@@ -145,59 +425,47 @@ public class IvyXmlModuleDescriptorParser extends AbstractModuleDescriptorParser
         private String descriptorVersion;
         private String[] publicationsDefaultConf;
 
-        public Parser(ModuleDescriptorParser moduleDescriptorParser, ParserSettings ivySettings, Resource res) {
-            super(moduleDescriptorParser);
+        public Parser(IvyXmlModuleDescriptorParser moduleDescriptorParser, DescriptorParseContext ivySettings, ExternalResource res, URL descriptorURL) {
+            super(moduleDescriptorParser, res);
             parserSettings = ivySettings;
-            setResource(res);
+            this.descriptorURL = descriptorURL;
         }
         
-        public Parser newParser(Resource res) {
-            Parser parser = new Parser(getModuleDescriptorParser(), parserSettings, res);
+        public Parser newParser(ExternalResource res, URL descriptorURL) {
+            Parser parser = new Parser(getModuleDescriptorParser(), parserSettings, res, descriptorURL);
             parser.setValidate(validate);
             return parser;
         }
 
-        public void setInput(InputStream descriptorInput) {
-            this.descriptorInput = descriptorInput;
-        }
-
-        public void setInput(URL descriptorURL) {
-            this.descriptorURL = descriptorURL;
-        }
-
         public void setValidate(boolean validate) {
             this.validate = validate;
         }
 
-        public void parse() throws ParseException,
-                IOException {
-            try {
-                URL schemaURL = validate ? getSchemaURL() : null;
-                if (descriptorURL != null) {
-                    XMLHelper.parse(descriptorURL, schemaURL, this);
-                } else {
-                    XMLHelper.parse(descriptorInput, schemaURL, this, null);
-                }
-                checkConfigurations();
-                replaceConfigurationWildcards();
-                getMd().setModuleArtifact(DefaultArtifact.newIvyArtifact(getMd().getResolvedModuleRevisionId(), getMd().getPublicationDate()));
-                if (!artifactsDeclared) {
-                    String[] configurationNames = getMd().getConfigurationsNames();
-                    for (String configurationName : configurationNames) {
-                        getMd().addArtifact(configurationName, new MDArtifact(getMd(), getMd().getModuleRevisionId().getName(), "jar", "jar"));
+        public void parse() throws ParseException, IOException {
+            getResource().withContent(new Action<InputStream>() {
+                public void execute(InputStream inputStream) {
+                    URL schemaURL = validate ? getSchemaURL() : null;
+                    InputSource inSrc = new InputSource(inputStream);
+                    inSrc.setSystemId(descriptorURL.toExternalForm());
+                    try {
+                        XMLHelper.parse(inSrc, schemaURL, Parser.this, null);
+                    } catch (Exception e) {
+                        throw new MetaDataParseException("Ivy file", getResource(), e);
                     }
                 }
-                getMd().check();
-            } catch (ParserConfigurationException ex) {
-                IllegalStateException ise = new IllegalStateException(ex.getMessage() + " in " + descriptorURL);
-                ise.initCause(ex);
-                throw ise;
-            } catch (Exception ex) {
-                checkErrors();
-                ParseException pe = new ParseException(ex.getMessage() + " in " + descriptorURL, 0);
-                pe.initCause(ex);
-                throw pe;
+            });
+            checkErrors();
+            checkConfigurations();
+            replaceConfigurationWildcards();
+            getMd().setModuleArtifact(DefaultArtifact.newIvyArtifact(getMd().getResolvedModuleRevisionId(), getMd().getPublicationDate()));
+            if (!artifactsDeclared) {
+                String[] configurationNames = getMd().getConfigurationsNames();
+                for (String configurationName : configurationNames) {
+                    getMd().addArtifact(configurationName, new MDArtifact(getMd(), getMd().getModuleRevisionId().getName(), "jar", "jar"));
+                }
             }
+            checkErrors();
+            getMd().check();
         }
 
         public void startElement(String uri, String localName, String qName, Attributes attributes)
@@ -214,7 +482,11 @@ public class IvyXmlModuleDescriptorParser extends AbstractModuleDescriptorParser
                     extendsStarted(attributes);
                 } else if (state == State.INFO && "license".equals(qName)) {
                     getMd().addLicense(new License(substitute(attributes.getValue("name")), substitute(attributes.getValue("url"))));
-                } else if (state == State.INFO && "description".equals(qName)) {
+                } /* CHECKSTYLE:OFF */ else if (state == State.INFO && "ivyauthor".equals(qName)) {
+                    // nothing to do, we don't store this
+                } else if (state == State.INFO && "repository".equals(qName)) {
+                    // nothing to do, we don't store this
+                } /* CHECKSTYLE:ON */ else if (state == State.INFO && "description".equals(qName)) {
                     getMd().setHomePage(substitute(attributes.getValue("homepage")));
                     state = State.DESCRIPTION;
                     buffer = new StringBuffer();
@@ -229,7 +501,7 @@ public class IvyXmlModuleDescriptorParser extends AbstractModuleDescriptorParser
                     dependenciesStarted(attributes);
                 } else if ("conflicts".equals(qName)) {
                     if (!descriptorVersion.startsWith("1.")) {
-                        DeprecationLogger.nagUserWith("Using conflicts section in ivy.xml is deprecated: please use hints section instead. Ivy file URL: " + descriptorURL);
+                        DeprecationLogger.nagUserWith("Using conflicts section in ivy.xml is deprecated: please use hints section instead. Ivy file: " + getResource().getName());
                     }
                     state = State.CONFLICT;
                     checkConfigurations();
@@ -348,7 +620,6 @@ public class IvyXmlModuleDescriptorParser extends AbstractModuleDescriptorParser
                     mergeDescription(parent.getDescription());
                 }
             }
-
         }
 
         private void mergeAll(ModuleDescriptor parent) {
@@ -422,7 +693,7 @@ public class IvyXmlModuleDescriptorParser extends AbstractModuleDescriptorParser
                 throws ParseException, IOException {
             URL url = relativeUrlResolver.getURL(descriptorURL, location);
             LOGGER.debug("Trying to load included ivy file from " + url.toString());
-            Parser parser = newParser(new URLResource(url));
+            Parser parser = newParser(new UrlExternalResource(url), url);
             parser.parse();
             return parser.getModuleDescriptor();
         }
@@ -432,27 +703,7 @@ public class IvyXmlModuleDescriptorParser extends AbstractModuleDescriptorParser
             ModuleId parentModuleId = new ModuleId(parentOrganisation, parentModule);
             ModuleRevisionId parentMrid = new ModuleRevisionId(parentModuleId, parentRevision);
 
-            DependencyDescriptor dd = new DefaultDependencyDescriptor(parentMrid, true);
-            ResolveData data = IvyContext.getContext().getResolveData();
-            if (data == null) {
-                ResolveEngine engine = IvyContext.getContext().getIvy().getResolveEngine();
-                ResolveOptions options = new ResolveOptions();
-                options.setDownload(false);
-                data = new ResolveData(engine, options);
-            }
-
-            DependencyResolver resolver = parserSettings.getResolver(parentMrid);
-            if (resolver == null) {
-                // TODO: Throw exception here?
-                return null;
-            } else {
-                dd = NameSpaceHelper.toSystem(dd, parserSettings.getContextNamespace());
-                ResolvedModuleRevision otherModule = resolver.getDependency(dd, data);
-                if (otherModule == null) {
-                    throw new ParseException("Unable to find " + parentMrid.toString(), 0);
-                }
-                return otherModule.getDescriptor();
-            }
+            return parserSettings.getModuleDescriptor(parentMrid);
         }
 
         private void publicationsStarted(Attributes attributes) {
@@ -478,8 +729,7 @@ public class IvyXmlModuleDescriptorParser extends AbstractModuleDescriptorParser
 
             // create a new temporary parser to read the configurations from
             // the specified file.
-            Parser parser = newParser(new URLResource(url));
-            parser.setInput(url);
+            Parser parser = newParser(new UrlExternalResource(url), url);
             XMLHelper.parse(url , null, parser);
 
             // add the configurations from this temporary parser to this module descriptor
@@ -636,7 +886,7 @@ public class IvyXmlModuleDescriptorParser extends AbstractModuleDescriptorParser
                 setDefaultConf(defaultConf);
             }
             String defaultConfMapping = substitute(attributes.getValue("defaultconfmapping"));
-            if (defaultConf != null) {
+            if (defaultConfMapping != null) {
                 setDefaultConfMapping(defaultConfMapping);
             }
             String confMappingOverride = substitute(attributes.getValue("confmappingoverride"));
@@ -662,17 +912,7 @@ public class IvyXmlModuleDescriptorParser extends AbstractModuleDescriptorParser
             Map extraAttributes = getExtraAttributes(attributes, new String[]{"organisation", "module", "revision", "status", "publication", "branch", "namespace", "default", "resolver"});
             getMd().setModuleRevisionId(ModuleRevisionId.newInstance(org, module, branch, revision, extraAttributes));
 
-            String namespace = substitute(attributes.getValue("namespace"));
-            if (namespace != null) {
-                Namespace ns = parserSettings.getNamespace(namespace);
-                if (ns == null) {
-                    LOGGER.warn("namespace not found for " + getMd().getModuleRevisionId() + ": " + namespace);
-                } else {
-                    getMd().setNamespace(ns);
-                }
-            }
-
-            getMd().setStatus(elvis(substitute(attributes.getValue("status")), parserSettings.getStatusManager().getDefaultStatus()));
+            getMd().setStatus(elvis(substitute(attributes.getValue("status")), parserSettings.getDefaultStatus()));
             getMd().setDefault(Boolean.valueOf(substitute(attributes.getValue("default"))));
             String pubDate = substitute(attributes.getValue("publication"));
             if (pubDate != null && pubDate.length() > 0) {
@@ -893,7 +1133,9 @@ public class IvyXmlModuleDescriptorParser extends AbstractModuleDescriptorParser
         }
 
         private URL getSchemaURL() {
-            return getClass().getResource("ivy.xsd");
+            URL resource = getClass().getClassLoader().getResource("org/apache/ivy/plugins/parser/xml/ivy.xsd");
+            assert resource != null;
+            return resource;
         }
 
         private String elvis(String value, String defaultValue) {
@@ -905,7 +1147,19 @@ public class IvyXmlModuleDescriptorParser extends AbstractModuleDescriptorParser
         }
 
         private Map getExtraAttributes(Attributes attributes, String[] ignoredAttributeNames) {
-            return ExtendableItemHelper.getExtraAttributes(parserSettings, attributes, ignoredAttributeNames);
+            return getExtraAttributes(parserSettings, attributes, ignoredAttributeNames);
+        }
+
+        public static Map getExtraAttributes(
+                DescriptorParseContext settings, Attributes attributes, String[] ignoredAttNames) {
+            Map ret = new HashMap();
+            Collection ignored = Arrays.asList(ignoredAttNames);
+            for (int i = 0; i < attributes.getLength(); i++) {
+                if (!ignored.contains(attributes.getQName(i))) {
+                    ret.put(attributes.getQName(i), settings.substitute(attributes.getValue(i)));
+                }
+            }
+            return ret;
         }
 
         private void fillExtraAttributes(DefaultExtendableItem item, Attributes attributes, String[] ignoredAttNames) {
diff --git a/subprojects/core-impl/src/main/groovy/org/gradle/api/internal/artifacts/ivyservice/ivyresolve/parser/MetaDataParseException.java b/subprojects/core-impl/src/main/groovy/org/gradle/api/internal/artifacts/ivyservice/ivyresolve/parser/MetaDataParseException.java
new file mode 100644
index 0000000..f849117
--- /dev/null
+++ b/subprojects/core-impl/src/main/groovy/org/gradle/api/internal/artifacts/ivyservice/ivyresolve/parser/MetaDataParseException.java
@@ -0,0 +1,32 @@
+/*
+ * Copyright 2013 the original author or authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY 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.ivyresolve.parser;
+
+import org.gradle.api.GradleException;
+import org.gradle.api.internal.Contextual;
+import org.gradle.api.internal.externalresource.ExternalResource;
+
+ at Contextual
+public class MetaDataParseException extends GradleException {
+    public MetaDataParseException(String message) {
+        super(message);
+    }
+
+    public MetaDataParseException(String typeName, ExternalResource resource, Throwable cause) {
+        super(String.format("Could not parse %s %s", typeName, resource.getName()), cause);
+    }
+}
diff --git a/subprojects/core-impl/src/main/groovy/org/gradle/api/internal/artifacts/ivyservice/ivyresolve/parser/MetaDataParser.java b/subprojects/core-impl/src/main/groovy/org/gradle/api/internal/artifacts/ivyservice/ivyresolve/parser/MetaDataParser.java
new file mode 100644
index 0000000..b23ac04
--- /dev/null
+++ b/subprojects/core-impl/src/main/groovy/org/gradle/api/internal/artifacts/ivyservice/ivyresolve/parser/MetaDataParser.java
@@ -0,0 +1,23 @@
+/*
+ * Copyright 2013 the original author or authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY 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.ivyresolve.parser;
+
+import org.gradle.api.internal.artifacts.ivyservice.ivyresolve.MutableModuleVersionMetaData;
+import org.gradle.api.internal.externalresource.LocallyAvailableExternalResource;
+
+public interface MetaDataParser {
+    MutableModuleVersionMetaData parseModuleMetaData(LocallyAvailableExternalResource resource, DescriptorParseContext context) throws MetaDataParseException;
+}
diff --git a/subprojects/core-impl/src/main/groovy/org/gradle/api/internal/artifacts/ivyservice/ivyresolve/parser/ModuleDescriptorAdapter.java b/subprojects/core-impl/src/main/groovy/org/gradle/api/internal/artifacts/ivyservice/ivyresolve/parser/ModuleDescriptorAdapter.java
new file mode 100644
index 0000000..5580268
--- /dev/null
+++ b/subprojects/core-impl/src/main/groovy/org/gradle/api/internal/artifacts/ivyservice/ivyresolve/parser/ModuleDescriptorAdapter.java
@@ -0,0 +1,87 @@
+/*
+ * Copyright 2013 the original author or authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY 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.ivyresolve.parser;
+
+import org.apache.ivy.core.module.descriptor.ModuleDescriptor;
+import org.apache.ivy.core.module.id.ModuleRevisionId;
+import org.gradle.api.artifacts.ModuleVersionIdentifier;
+import org.gradle.api.internal.artifacts.DefaultModuleVersionIdentifier;
+import org.gradle.api.internal.artifacts.ivyservice.ivyresolve.ConfigurationMetaData;
+import org.gradle.api.internal.artifacts.ivyservice.ivyresolve.DependencyMetaData;
+import org.gradle.api.internal.artifacts.ivyservice.ivyresolve.MutableModuleVersionMetaData;
+
+import java.util.Arrays;
+import java.util.List;
+
+public class ModuleDescriptorAdapter implements MutableModuleVersionMetaData {
+    private static final List<String> DEFAULT_STATUS_SCHEME = Arrays.asList("integration", "milestone", "release");
+
+    private final ModuleVersionIdentifier moduleVersionIdentifier;
+    private final ModuleDescriptor moduleDescriptor;
+    private boolean changing;
+    private String status;
+    private List<String> statusScheme = DEFAULT_STATUS_SCHEME;
+
+    public ModuleDescriptorAdapter(ModuleRevisionId moduleRevisionId, ModuleDescriptor moduleDescriptor) {
+        this.moduleVersionIdentifier = DefaultModuleVersionIdentifier.newId(moduleRevisionId);
+        this.moduleDescriptor = moduleDescriptor;
+        status = moduleDescriptor.getStatus();
+    }
+
+    public ModuleVersionIdentifier getId() {
+        return moduleVersionIdentifier;
+    }
+
+    public ModuleDescriptor getDescriptor() {
+        return moduleDescriptor;
+    }
+
+    public boolean isChanging() {
+        return changing;
+    }
+
+    public String getStatus() {
+        return status;
+    }
+
+    public List<String> getStatusScheme() {
+        return statusScheme;
+    }
+
+    public void setChanging(boolean changing) {
+        this.changing = changing;
+    }
+
+    public void setStatus(String status) {
+        this.status = status;
+    }
+
+    public void setStatusScheme(List<String> statusScheme) {
+        this.statusScheme = statusScheme;
+    }
+
+    // The methods will require implementing when we stop handing around ModuleDescriptor and make ModuleVersionMetaData our key internal API.
+    public List<DependencyMetaData> getDependencies() {
+        // TODO:DAZ
+        throw new UnsupportedOperationException();
+    }
+
+    public ConfigurationMetaData getConfiguration(String name) {
+        // TODO:DAZ
+        throw new UnsupportedOperationException();
+    }
+}
diff --git a/subprojects/core-impl/src/main/groovy/org/gradle/api/internal/artifacts/ivyservice/ivyresolve/parser/ModuleDescriptorParser.java b/subprojects/core-impl/src/main/groovy/org/gradle/api/internal/artifacts/ivyservice/ivyresolve/parser/ModuleDescriptorParser.java
new file mode 100644
index 0000000..2d5549c
--- /dev/null
+++ b/subprojects/core-impl/src/main/groovy/org/gradle/api/internal/artifacts/ivyservice/ivyresolve/parser/ModuleDescriptorParser.java
@@ -0,0 +1,31 @@
+/*
+ * Copyright 2013 the original author or authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY 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.ivyresolve.parser;
+
+import org.apache.ivy.core.module.descriptor.ModuleDescriptor;
+import org.gradle.api.internal.externalresource.ExternalResource;
+import org.gradle.api.internal.externalresource.LocallyAvailableExternalResource;
+
+import java.io.File;
+
+public interface ModuleDescriptorParser {
+    public ModuleDescriptor parseDescriptor(DescriptorParseContext ivySettings, File descriptorFile, boolean validate) throws MetaDataParseException;
+
+    public ModuleDescriptor parseDescriptor(DescriptorParseContext ivySettings, LocallyAvailableExternalResource resource, boolean validate) throws MetaDataParseException;
+
+    public boolean accept(ExternalResource res);
+}
diff --git a/subprojects/core-impl/src/main/groovy/org/gradle/api/internal/artifacts/ivyservice/ivyresolve/parser/ModuleScopedParserSettings.java b/subprojects/core-impl/src/main/groovy/org/gradle/api/internal/artifacts/ivyservice/ivyresolve/parser/ModuleScopedParserSettings.java
deleted file mode 100644
index 82905c7..0000000
--- a/subprojects/core-impl/src/main/groovy/org/gradle/api/internal/artifacts/ivyservice/ivyresolve/parser/ModuleScopedParserSettings.java
+++ /dev/null
@@ -1,102 +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.api.internal.artifacts.ivyservice.ivyresolve.parser;
-
-import org.apache.ivy.core.RelativeUrlResolver;
-import org.apache.ivy.core.cache.ResolutionCacheManager;
-import org.apache.ivy.core.module.id.ModuleId;
-import org.apache.ivy.core.module.id.ModuleRevisionId;
-import org.apache.ivy.core.module.status.StatusManager;
-import org.apache.ivy.plugins.conflict.ConflictManager;
-import org.apache.ivy.plugins.matcher.PatternMatcher;
-import org.apache.ivy.plugins.namespace.Namespace;
-import org.apache.ivy.plugins.parser.ParserSettings;
-import org.apache.ivy.plugins.resolver.DependencyResolver;
-
-import java.io.File;
-import java.util.Map;
-
-/**
- * ParserSettings that control the scope of searches carried out during parsing.
- * If the parser asks for a resolver for the currently resolving revision, the resolver scope is only the repository where the module was resolved.
- * If the parser asks for a resolver for a different revision, the resolver scope is all repositories.
- */
-public class ModuleScopedParserSettings implements ParserSettings {
-    private final ParserSettings settings;
-    private final DependencyResolver currentResolver;
-    private final ModuleRevisionId currentRevisionId;
-
-    public ModuleScopedParserSettings(ParserSettings settings, DependencyResolver currentResolver, ModuleRevisionId currentRevisionId) {
-        this.settings = settings;
-        this.currentResolver = currentResolver;
-        this.currentRevisionId = currentRevisionId;
-    }
-
-    public ModuleRevisionId getCurrentRevisionId() {
-        return currentRevisionId;
-    }
-
-    public DependencyResolver getResolver(ModuleRevisionId mRevId) {
-        if (mRevId.equals(currentRevisionId)) {
-            return currentResolver;
-        }
-        return settings.getResolver(mRevId);
-    }
-
-    public ConflictManager getConflictManager(String name) {
-        return settings.getConflictManager(name);
-    }
-
-    public String substitute(String value) {
-        return settings.substitute(value);
-    }
-
-    public Map substitute(Map strings) {
-        return settings.substitute(strings);
-    }
-
-    public ResolutionCacheManager getResolutionCacheManager() {
-        return settings.getResolutionCacheManager();
-    }
-
-    public PatternMatcher getMatcher(String matcherName) {
-        return settings.getMatcher(matcherName);
-    }
-
-    public Namespace getNamespace(String namespace) {
-        return settings.getNamespace(namespace);
-    }
-
-    public StatusManager getStatusManager() {
-        return settings.getStatusManager();
-    }
-
-    public RelativeUrlResolver getRelativeUrlResolver() {
-        return settings.getRelativeUrlResolver();
-    }
-
-    public File resolveFile(String filename) {
-        return settings.resolveFile(filename);
-    }
-
-    public String getDefaultBranch(ModuleId moduleId) {
-        return settings.getDefaultBranch(moduleId);
-    }
-
-    public Namespace getContextNamespace() {
-        return settings.getContextNamespace();
-    }
-}
diff --git a/subprojects/core-impl/src/main/groovy/org/gradle/api/internal/artifacts/ivyservice/ivyresolve/parser/ParserRegistry.java b/subprojects/core-impl/src/main/groovy/org/gradle/api/internal/artifacts/ivyservice/ivyresolve/parser/ParserRegistry.java
index efd9040..5e9d444 100644
--- a/subprojects/core-impl/src/main/groovy/org/gradle/api/internal/artifacts/ivyservice/ivyresolve/parser/ParserRegistry.java
+++ b/subprojects/core-impl/src/main/groovy/org/gradle/api/internal/artifacts/ivyservice/ivyresolve/parser/ParserRegistry.java
@@ -15,8 +15,7 @@
  */
 package org.gradle.api.internal.artifacts.ivyservice.ivyresolve.parser;
 
-import org.apache.ivy.plugins.parser.ModuleDescriptorParser;
-import org.apache.ivy.plugins.repository.Resource;
+import org.gradle.api.internal.externalresource.ExternalResource;
 
 import java.util.ArrayList;
 import java.util.List;
@@ -29,7 +28,7 @@ public class ParserRegistry {
         parsers.add(new DownloadedIvyModuleDescriptorParser());
     }
 
-    public ModuleDescriptorParser forResource(Resource resource) {
+    public ModuleDescriptorParser forResource(ExternalResource resource) {
         for (ModuleDescriptorParser parser : parsers) {
             if (parser.accept(resource)) {
                 return parser;
diff --git a/subprojects/core-impl/src/main/groovy/org/gradle/api/internal/artifacts/ivyservice/ivyresolve/parser/PomReader.java b/subprojects/core-impl/src/main/groovy/org/gradle/api/internal/artifacts/ivyservice/ivyresolve/parser/PomReader.java
new file mode 100644
index 0000000..53c7e59
--- /dev/null
+++ b/subprojects/core-impl/src/main/groovy/org/gradle/api/internal/artifacts/ivyservice/ivyresolve/parser/PomReader.java
@@ -0,0 +1,577 @@
+/*
+ * Copyright 2013 the original author or authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY 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.ivyresolve.parser;
+
+import org.apache.ivy.core.IvyPatternHelper;
+import org.apache.ivy.core.module.descriptor.License;
+import org.apache.ivy.core.module.id.ModuleId;
+import org.apache.ivy.core.module.id.ModuleRevisionId;
+import org.apache.ivy.plugins.parser.m2.PomDependencyMgt;
+import org.apache.ivy.util.XMLHelper;
+import org.gradle.api.Transformer;
+import org.gradle.api.internal.externalresource.LocallyAvailableExternalResource;
+import org.w3c.dom.Document;
+import org.w3c.dom.Element;
+import org.w3c.dom.Node;
+import org.w3c.dom.NodeList;
+import org.xml.sax.EntityResolver;
+import org.xml.sax.InputSource;
+import org.xml.sax.SAXException;
+import org.xml.sax.SAXParseException;
+
+import javax.xml.parsers.DocumentBuilder;
+import java.io.*;
+import java.util.*;
+
+
+/**
+ * Copied from org.apache.ivy.plugins.parser.m2.PomReader.
+ */
+public class PomReader {
+
+    private static final String PACKAGING = "packaging";
+    private static final String DEPENDENCY = "dependency";
+    private static final String DEPENDENCIES = "dependencies";
+    private static final String DEPENDENCY_MGT = "dependencyManagement";
+    private static final String PROJECT = "project";
+    private static final String MODEL = "model";
+    private static final String GROUP_ID = "groupId";
+    private static final String ARTIFACT_ID = "artifactId";
+    private static final String VERSION = "version";
+    private static final String DESCRIPTION = "description";
+    private static final String HOMEPAGE = "url";
+    private static final String LICENSES = "licenses";
+    private static final String LICENSE = "license";
+    private static final String LICENSE_NAME = "name";
+    private static final String LICENSE_URL = "url";
+    private static final String PARENT = "parent";
+    private static final String SCOPE = "scope";
+    private static final String CLASSIFIER = "classifier";
+    private static final String OPTIONAL = "optional";
+    private static final String EXCLUSIONS = "exclusions";
+    private static final String EXCLUSION = "exclusion";
+    private static final String DISTRIBUTION_MGT = "distributionManagement";
+    private static final String RELOCATION = "relocation";
+    private static final String PROPERTIES = "properties";
+    private static final String PLUGINS = "plugins";
+    private static final String PLUGIN = "plugin";
+    private static final String TYPE = "type";
+
+    private Map<String, String> properties = new HashMap<String, String>();
+
+    private final Element projectElement;
+    private final Element parentElement;
+
+    public PomReader(final LocallyAvailableExternalResource resource) throws IOException, SAXException {
+        final String systemId = resource.getLocalResource().getFile().toURI().toASCIIString();
+        Document pomDomDoc = resource.withContent(new Transformer<Document, InputStream>() {
+            public Document transform(InputStream inputStream) {
+                try {
+                    return parseToDom(inputStream, systemId);
+                } catch (Exception e) {
+                    throw new MetaDataParseException("POM", resource, e);
+                }
+            }
+        });
+        projectElement = pomDomDoc.getDocumentElement();
+        if (!PROJECT.equals(projectElement.getNodeName()) && !MODEL.equals(projectElement.getNodeName())) {
+            throw new SAXParseException("project must be the root tag", systemId, systemId, 0, 0);
+        }
+        parentElement = getFirstChildElement(projectElement, PARENT);
+    }
+
+    public static Document parseToDom(InputStream stream, String systemId) throws IOException, SAXException {
+        EntityResolver entityResolver = new EntityResolver() {
+            public InputSource resolveEntity(String publicId, String systemId)
+                    throws SAXException, IOException {
+                if ((systemId != null) && systemId.endsWith("m2-entities.ent")) {
+                    return new InputSource(org.apache.ivy.plugins.parser.m2.PomReader.class.getResourceAsStream("m2-entities.ent"));
+                }
+                return null;
+            }
+        };
+        InputStream dtdStream = new AddDTDFilterInputStream(stream);
+        DocumentBuilder docBuilder = XMLHelper.getDocBuilder(entityResolver);
+        return docBuilder.parse(dtdStream, systemId);
+    }
+
+    public boolean hasParent() {
+        return parentElement != null;
+    }
+
+    /**
+     * Add a property if not yet set and value is not null.
+     * This garantee that property keep the first value that is put on it and that the properties
+     * are never null.
+     */
+    public void setProperty(String prop, String val) {
+        if (!properties.containsKey(prop) && val != null) {
+            properties.put(prop, val);
+        }
+    }
+
+
+    public String getGroupId() {
+        String groupId = getFirstChildText(projectElement , GROUP_ID);
+        if (groupId == null) {
+            groupId = getFirstChildText(parentElement, GROUP_ID);
+        }
+        return replaceProps(groupId);
+
+    }
+
+    public String getParentGroupId() {
+        String groupId = getFirstChildText(parentElement , GROUP_ID);
+        if (groupId == null) {
+            groupId = getFirstChildText(projectElement, GROUP_ID);
+        }
+        return replaceProps(groupId);
+    }
+
+
+
+    public String getArtifactId() {
+        String val = getFirstChildText(projectElement , ARTIFACT_ID);
+        if (val == null) {
+            val = getFirstChildText(parentElement, ARTIFACT_ID);
+        }
+        return replaceProps(val);
+    }
+
+    public String getParentArtifactId() {
+        String val = getFirstChildText(parentElement , ARTIFACT_ID);
+        if (val == null) {
+            val = getFirstChildText(projectElement, ARTIFACT_ID);
+        }
+        return replaceProps(val);
+    }
+
+
+    public String getVersion() {
+        String val = getFirstChildText(projectElement , VERSION);
+        if (val == null) {
+            val = getFirstChildText(parentElement, VERSION);
+        }
+        return replaceProps(val);
+    }
+
+    public String getParentVersion() {
+        String val = getFirstChildText(parentElement , VERSION);
+        if (val == null) {
+            val = getFirstChildText(projectElement, VERSION);
+        }
+        return replaceProps(val);
+    }
+
+
+    public String getPackaging() {
+        String val = getFirstChildText(projectElement , PACKAGING);
+        if (val == null) {
+            val = "jar";
+        }
+        return val;
+    }
+
+    public String getHomePage() {
+        String val = getFirstChildText(projectElement , HOMEPAGE);
+        if (val == null) {
+            val = "";
+        }
+        return val;
+    }
+
+    public String getDescription() {
+        String val = getFirstChildText(projectElement , DESCRIPTION);
+        if (val == null) {
+            val = "";
+        }
+        return val.trim();
+    }
+
+    public License[] getLicenses() {
+        Element licenses = getFirstChildElement(projectElement, LICENSES);
+        if (licenses == null) {
+            return new License[0];
+        }
+        licenses.normalize();
+        List<License> lics = new ArrayList<License>();
+        for (Element license : getAllChilds(licenses)) {
+            if (LICENSE.equals(license.getNodeName())) {
+                String name = getFirstChildText(license, LICENSE_NAME);
+                String url = getFirstChildText(license, LICENSE_URL);
+
+                if ((name == null) && (url == null)) {
+                    // move to next license
+                    continue;
+                }
+
+                if (name == null) {
+                    // The license name is required in Ivy but not in a POM!
+                    name = "Unknown License";
+                }
+
+                lics.add(new License(name, url));
+            }
+        }
+        return lics.toArray(new License[lics.size()]);
+    }
+
+
+    public ModuleRevisionId getRelocation() {
+        Element distrMgt = getFirstChildElement(projectElement, DISTRIBUTION_MGT);
+        Element relocation = getFirstChildElement(distrMgt , RELOCATION);
+        if (relocation == null) {
+            return null;
+        } else {
+            String relocGroupId = getFirstChildText(relocation, GROUP_ID);
+            String relocArtId = getFirstChildText(relocation, ARTIFACT_ID);
+            String relocVersion = getFirstChildText(relocation, VERSION);
+            relocGroupId = relocGroupId == null ? getGroupId() : relocGroupId;
+            relocArtId = relocArtId == null ? getArtifactId() : relocArtId;
+            relocVersion = relocVersion == null ? getVersion() : relocVersion;
+            return ModuleRevisionId.newInstance(relocGroupId, relocArtId, relocVersion);
+        }
+    }
+
+    public List<PomDependencyData> getDependencies() {
+        Element dependenciesElement = getFirstChildElement(projectElement, DEPENDENCIES);
+        List<PomDependencyData> dependencies = new LinkedList<PomDependencyData>();
+        if (dependenciesElement != null) {
+            NodeList childs = dependenciesElement.getChildNodes();
+            for (int i = 0; i < childs.getLength(); i++) {
+                Node node = childs.item(i);
+                if (node instanceof Element && DEPENDENCY.equals(node.getNodeName())) {
+                    dependencies.add(new PomDependencyData((Element) node));
+                }
+            }
+        }
+        return dependencies;
+    }
+
+
+    public List<PomDependencyMgt> getDependencyMgt() {
+        Element dependenciesElement = getFirstChildElement(projectElement, DEPENDENCY_MGT);
+        dependenciesElement = getFirstChildElement(dependenciesElement, DEPENDENCIES);
+        List<PomDependencyMgt> dependencies = new LinkedList<PomDependencyMgt>();
+        if (dependenciesElement != null) {
+            NodeList childs = dependenciesElement.getChildNodes();
+            for (int i = 0; i < childs.getLength(); i++) {
+                Node node = childs.item(i);
+                if (node instanceof Element && DEPENDENCY.equals(node.getNodeName())) {
+                    dependencies.add(new PomDependencyMgtElement((Element) node));
+                }
+            }
+        }
+        return dependencies;
+    }
+
+    public class PomDependencyMgtElement implements PomDependencyMgt {
+        private final Element depElement;
+
+        PomDependencyMgtElement(Element depElement) {
+            this.depElement = depElement;
+        }
+
+        /* (non-Javadoc)
+         * @see org.apache.ivy.plugins.parser.m2.PomDependencyMgt#getGroupId()
+         */
+        public String getGroupId() {
+            String val = getFirstChildText(depElement , GROUP_ID);
+            return replaceProps(val);
+        }
+
+        /* (non-Javadoc)
+         * @see org.apache.ivy.plugins.parser.m2.PomDependencyMgt#getArtifaceId()
+         */
+        public String getArtifactId() {
+            String val = getFirstChildText(depElement , ARTIFACT_ID);
+            return replaceProps(val);
+        }
+
+        /* (non-Javadoc)
+         * @see org.apache.ivy.plugins.parser.m2.PomDependencyMgt#getVersion()
+         */
+        public String getVersion() {
+            String val = getFirstChildText(depElement , VERSION);
+            return replaceProps(val);
+        }
+
+        public String getScope() {
+            String val = getFirstChildText(depElement , SCOPE);
+            return replaceProps(val);
+        }
+
+        public List<ModuleId> getExcludedModules() {
+            Element exclusionsElement = getFirstChildElement(depElement, EXCLUSIONS);
+            List<ModuleId> exclusions = new LinkedList<ModuleId>();
+            if (exclusionsElement != null) {
+                NodeList childs = exclusionsElement.getChildNodes();
+                for (int i = 0; i < childs.getLength(); i++) {
+                    Node node = childs.item(i);
+                    if (node instanceof Element && EXCLUSION.equals(node.getNodeName())) {
+                        String groupId = getFirstChildText((Element) node, GROUP_ID);
+                        String artifactId = getFirstChildText((Element) node, ARTIFACT_ID);
+                        if ((groupId != null) && (artifactId != null)) {
+                            exclusions.add(ModuleId.newInstance(groupId, artifactId));
+                        }
+                    }
+                }
+            }
+            return exclusions;
+        }
+    }
+
+    public List<PomPluginElement> getPlugins() {
+        List<PomPluginElement> plugins = new LinkedList<PomPluginElement>();
+
+        Element buildElement = getFirstChildElement(projectElement, "build");
+        if (buildElement == null) {
+            return plugins;
+        }
+
+        Element pluginsElement = getFirstChildElement(buildElement, PLUGINS);
+        if (pluginsElement != null) {
+            NodeList childs = pluginsElement.getChildNodes();
+            for (int i = 0; i < childs.getLength(); i++) {
+                Node node = childs.item(i);
+                if (node instanceof Element && PLUGIN.equals(node.getNodeName())) {
+                    plugins.add(new PomPluginElement((Element) node));
+                }
+            }
+        }
+        return plugins;
+    }
+
+    public class PomPluginElement implements PomDependencyMgt {
+        private Element pluginElement;
+
+        PomPluginElement(Element pluginElement) {
+            this.pluginElement = pluginElement;
+        }
+
+        public String getGroupId() {
+            String val = getFirstChildText(pluginElement , GROUP_ID);
+            return replaceProps(val);
+        }
+
+        public String getArtifactId() {
+            String val = getFirstChildText(pluginElement , ARTIFACT_ID);
+            return replaceProps(val);
+        }
+
+        public String getVersion() {
+            String val = getFirstChildText(pluginElement , VERSION);
+            return replaceProps(val);
+        }
+
+        public String getScope() {
+            return null; // not used
+        }
+
+        public List<ModuleId> getExcludedModules() {
+            return Collections.emptyList(); // probably not used?
+        }
+    }
+
+
+    public class PomDependencyData extends PomDependencyMgtElement {
+        private final Element depElement;
+        PomDependencyData(Element depElement) {
+            super(depElement);
+            this.depElement = depElement;
+        }
+
+        public String getScope() {
+            String val = getFirstChildText(depElement , SCOPE);
+            return replaceProps(val);
+        }
+
+        public String getClassifier() {
+            String val = getFirstChildText(depElement , CLASSIFIER);
+            return replaceProps(val);
+        }
+
+        public String getType() {
+            String val = getFirstChildText(depElement, TYPE);
+            return replaceProps(val);
+        }
+
+        public boolean isOptional() {
+            Element e = getFirstChildElement(depElement, OPTIONAL);
+            return (e != null) && "true".equalsIgnoreCase(getTextContent(e));
+        }
+    }
+
+    /**
+     * @return the content of the properties tag into the pom.
+     */
+    public Map<String, String> getPomProperties() {
+        Map<String, String> pomProperties = new HashMap<String, String>();
+        Element propsEl = getFirstChildElement(projectElement, PROPERTIES);
+        if (propsEl != null) {
+            propsEl.normalize();
+        }
+        for (Element prop : getAllChilds(propsEl)) {
+            pomProperties.put(prop.getNodeName(), getTextContent(prop));
+        }
+        return pomProperties;
+    }
+
+    private String replaceProps(String val) {
+        if (val == null) {
+            return null;
+        } else {
+            return IvyPatternHelper.substituteVariables(val, properties).trim();
+        }
+    }
+
+    private static String getTextContent(Element element) {
+        StringBuilder result = new StringBuilder();
+
+        NodeList childNodes = element.getChildNodes();
+        for (int i = 0; i < childNodes.getLength(); i++) {
+            Node child = childNodes.item(i);
+
+            switch (child.getNodeType()) {
+                case Node.CDATA_SECTION_NODE:
+                case Node.TEXT_NODE:
+                    result.append(child.getNodeValue());
+                    break;
+                default:
+                    break;
+            }
+        }
+
+        return result.toString();
+    }
+
+    private static String getFirstChildText(Element parentElem, String name) {
+        Element node = getFirstChildElement(parentElem, name);
+        if (node != null) {
+            return getTextContent(node);
+        } else {
+            return null;
+        }
+    }
+
+    private static Element getFirstChildElement(Element parentElem, String name) {
+        if (parentElem == null) {
+            return null;
+        }
+        NodeList childs = parentElem.getChildNodes();
+        for (int i = 0; i < childs.getLength(); i++) {
+            Node node = childs.item(i);
+            if (node instanceof Element && name.equals(node.getNodeName())) {
+                return (Element) node;
+            }
+        }
+        return null;
+    }
+
+    private static List<Element> getAllChilds(Element parent) {
+        List<Element> r = new LinkedList<Element>();
+        if (parent != null) {
+            NodeList childs = parent.getChildNodes();
+            for (int i = 0; i < childs.getLength(); i++) {
+                Node node = childs.item(i);
+                if (node instanceof Element) {
+                    r.add((Element) node);
+                }
+            }
+        }
+        return r;
+    }
+
+    private static final class AddDTDFilterInputStream extends FilterInputStream {
+        private static final int MARK = 10000;
+        private static final String DOCTYPE = "<!DOCTYPE project SYSTEM \"m2-entities.ent\">\n";
+
+        private int count;
+        private byte[] prefix = DOCTYPE.getBytes();
+
+        private AddDTDFilterInputStream(InputStream in) throws IOException {
+            super(new BufferedInputStream(in));
+
+            this.in.mark(MARK);
+
+            // TODO: we should really find a better solution for this...
+            // maybe we could use a FilterReader instead of a FilterInputStream?
+            int byte1 = this.in.read();
+            int byte2 = this.in.read();
+            int byte3 = this.in.read();
+
+            if (byte1 == 239 && byte2 == 187 && byte3 == 191) {
+                // skip the UTF-8 BOM
+                this.in.mark(MARK);
+            } else {
+                this.in.reset();
+            }
+
+            int bytesToSkip = 0;
+            LineNumberReader reader = new LineNumberReader(new InputStreamReader(this.in, "UTF-8"), 100);
+            String firstLine = reader.readLine();
+            if (firstLine != null) {
+                String trimmed = firstLine.trim();
+                if (trimmed.startsWith("<?xml ")) {
+                    int endIndex = trimmed.indexOf("?>");
+                    String xmlDecl = trimmed.substring(0, endIndex + 2);
+                    prefix = (xmlDecl + "\n" + DOCTYPE).getBytes();
+                    bytesToSkip = xmlDecl.getBytes().length;
+                }
+            }
+
+            this.in.reset();
+            for (int i = 0; i < bytesToSkip; i++) {
+                this.in.read();
+            }
+        }
+
+        public int read() throws IOException {
+            if (count < prefix.length) {
+                return prefix[count++];
+            }
+
+            return super.read();
+        }
+
+        public int read(byte[] b, int off, int len) throws IOException {
+            if (b == null) {
+                throw new NullPointerException();
+            } else if ((off < 0) || (off > b.length) || (len < 0)
+                    || ((off + len) > b.length) || ((off + len) < 0)) {
+                throw new IndexOutOfBoundsException();
+            } else if (len == 0) {
+                return 0;
+            }
+
+            int nbrBytesCopied = 0;
+
+            if (count < prefix.length) {
+                int nbrBytesFromPrefix = Math.min(prefix.length - count, len);
+                System.arraycopy(prefix, count, b, off, nbrBytesFromPrefix);
+                nbrBytesCopied = nbrBytesFromPrefix;
+            }
+
+            if (nbrBytesCopied < len) {
+                nbrBytesCopied += in.read(b, off + nbrBytesCopied, len - nbrBytesCopied);
+            }
+
+            count += nbrBytesCopied;
+            return nbrBytesCopied;
+        }
+    }
+
+}
diff --git a/subprojects/core-impl/src/main/groovy/org/gradle/api/internal/artifacts/ivyservice/ivyresolve/strategy/ChainVersionMatcher.java b/subprojects/core-impl/src/main/groovy/org/gradle/api/internal/artifacts/ivyservice/ivyresolve/strategy/ChainVersionMatcher.java
new file mode 100644
index 0000000..eb5f2c1
--- /dev/null
+++ b/subprojects/core-impl/src/main/groovy/org/gradle/api/internal/artifacts/ivyservice/ivyresolve/strategy/ChainVersionMatcher.java
@@ -0,0 +1,64 @@
+/*
+ * Copyright 2013 the original author or authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY 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.ivyresolve.strategy;
+
+import com.google.common.collect.Lists;
+import org.gradle.api.internal.artifacts.ivyservice.ivyresolve.ModuleVersionMetaData;
+
+import java.util.*;
+
+public class ChainVersionMatcher implements VersionMatcher {
+    private final List<VersionMatcher> matchers = Lists.newArrayList();
+
+    public void add(VersionMatcher matcher) {
+        matchers.add(matcher);
+    }
+
+    public boolean canHandle(String selector) {
+        // not expected to be called
+        throw new UnsupportedOperationException("canHandle");
+    }
+
+    public boolean isDynamic(String selector) {
+        return getCompatibleMatcher(selector).isDynamic(selector);
+    }
+
+    public boolean needModuleMetadata(String selector, String candidate) {
+        return getCompatibleMatcher(selector).needModuleMetadata(selector, candidate);
+    }
+
+    public boolean accept(String selector, String candidate) {
+        return getCompatibleMatcher(selector).accept(selector, candidate);
+    }
+
+    public boolean accept(String selector, ModuleVersionMetaData candidate) {
+        return getCompatibleMatcher(selector).accept(selector, candidate);
+    }
+
+    public int compare(String selector, String candidate) {
+        return getCompatibleMatcher(selector).compare(selector, candidate);
+    }
+
+    private VersionMatcher getCompatibleMatcher(String selector) {
+        for (VersionMatcher matcher : matchers) {
+            if (matcher.canHandle(selector)) {
+                return matcher;
+            }
+        }
+        throw new IllegalArgumentException("Invalid version selector: " + selector);
+    }
+}
+
diff --git a/subprojects/core-impl/src/main/groovy/org/gradle/api/internal/artifacts/ivyservice/ivyresolve/strategy/ExactVersionMatcher.java b/subprojects/core-impl/src/main/groovy/org/gradle/api/internal/artifacts/ivyservice/ivyresolve/strategy/ExactVersionMatcher.java
new file mode 100644
index 0000000..76fb959
--- /dev/null
+++ b/subprojects/core-impl/src/main/groovy/org/gradle/api/internal/artifacts/ivyservice/ivyresolve/strategy/ExactVersionMatcher.java
@@ -0,0 +1,113 @@
+/*
+ * Copyright 2013 the original author or authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY 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.ivyresolve.strategy;
+
+import com.google.common.collect.ImmutableMap;
+
+import org.gradle.api.internal.artifacts.ivyservice.ivyresolve.ModuleVersionMetaData;
+
+import java.util.Locale;
+import java.util.Map;
+
+/**
+ * Version matcher for "static" version selectors (1.0, 1.2.3, etc.).
+ */
+public class ExactVersionMatcher implements VersionMatcher {
+    private static final Map<String, Integer> SPECIAL_MEANINGS =
+            ImmutableMap.of("dev", new Integer(-1), "rc", new Integer(1), "final", new Integer(2));
+
+    public boolean canHandle(String selector) {
+        return true;
+    }
+
+    public boolean isDynamic(String selector) {
+        return false;
+    }
+
+    public boolean needModuleMetadata(String selector, String candidate) {
+        return false;
+    }
+
+    public boolean accept(String selector, String candidate) {
+        return selector.equals(candidate);
+    }
+
+    public boolean accept(String selector, ModuleVersionMetaData candidate) {
+        return accept(selector, candidate.getId().getVersion());
+    }
+
+    /**
+     * Compares a static selector with a candidate version. Algorithm is inspired
+     * by PHP version_compare one.
+     *
+     * TODO: compare() is inconsistent with accept(), because not everything
+     * that compares equal is accepted (e.g. 1.0 vs. 1_0). Can this cause problems?
+     */
+    public int compare(String selector, String candidate) {
+        if (selector.equals(candidate)) {
+            return 0;
+        }
+
+        selector = selector.replaceAll("([a-zA-Z])(\\d)", "$1.$2");
+        selector = selector.replaceAll("(\\d)([a-zA-Z])", "$1.$2");
+        candidate = candidate.replaceAll("([a-zA-Z])(\\d)", "$1.$2");
+        candidate = candidate.replaceAll("(\\d)([a-zA-Z])", "$1.$2");
+
+        String[] parts1 = selector.split("[\\._\\-\\+]");
+        String[] parts2 = candidate.split("[\\._\\-\\+]");
+
+        int i = 0;
+        for (; i < parts1.length && i < parts2.length; i++) {
+            if (parts1[i].equals(parts2[i])) {
+                continue;
+            }
+            boolean is1Number = isNumber(parts1[i]);
+            boolean is2Number = isNumber(parts2[i]);
+            if (is1Number && !is2Number) {
+                return 1;
+            }
+            if (is2Number && !is1Number) {
+                return -1;
+            }
+            if (is1Number && is2Number) {
+                return Long.valueOf(parts1[i]).compareTo(Long.valueOf(parts2[i]));
+            }
+            // both are strings, we compare them taking into account special meaning
+            Integer sm1 = SPECIAL_MEANINGS.get(parts1[i].toLowerCase(Locale.US));
+            Integer sm2 = SPECIAL_MEANINGS.get(parts2[i].toLowerCase(Locale.US));
+            if (sm1 != null) {
+                sm2 = sm2 == null ? 0 : sm2;
+                return sm1 - sm2;
+            }
+            if (sm2 != null) {
+                return -sm2;
+            }
+            return parts1[i].compareTo(parts2[i]);
+        }
+        if (i < parts1.length) {
+            return isNumber(parts1[i]) ? 1 : -1;
+        }
+        if (i < parts2.length) {
+            return isNumber(parts2[i]) ? -1 : 1;
+        }
+
+        return 0;
+    }
+
+    private boolean isNumber(String str) {
+        return str.matches("\\d+");
+    }
+}
\ No newline at end of file
diff --git a/subprojects/core-impl/src/main/groovy/org/gradle/api/internal/artifacts/ivyservice/ivyresolve/strategy/LatestStrategy.java b/subprojects/core-impl/src/main/groovy/org/gradle/api/internal/artifacts/ivyservice/ivyresolve/strategy/LatestStrategy.java
new file mode 100644
index 0000000..c442e20
--- /dev/null
+++ b/subprojects/core-impl/src/main/groovy/org/gradle/api/internal/artifacts/ivyservice/ivyresolve/strategy/LatestStrategy.java
@@ -0,0 +1,42 @@
+/*
+ * Copyright 2013 the original author or authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY 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.ivyresolve.strategy;
+
+import org.gradle.api.internal.artifacts.ivyservice.ivyresolve.Versioned;
+
+import java.util.Collection;
+import java.util.Comparator;
+import java.util.List;
+
+public interface LatestStrategy extends Comparator<Versioned> {
+    /**
+     * Finds the latest among the given versioned elements. The definition of 'latest'
+     * depends on the strategy itself.
+     */
+    <T extends Versioned> T findLatest(Collection<T> elements);
+
+    /**
+     * Sorts the given versioned elements from oldest to latest. The definition of
+     * 'latest' depends on the strategy itself.
+     */
+    <T extends Versioned> List<T> sort(Collection<T> elements);
+
+    /**
+     * Compares two versioned elements to see which is the 'latest'. The definition of
+     * 'latest' depends on the strategy itself.
+     */
+    int compare(Versioned element1, Versioned element2);
+}
diff --git a/subprojects/core-impl/src/main/groovy/org/gradle/api/internal/artifacts/ivyservice/ivyresolve/strategy/LatestVersionMatcher.java b/subprojects/core-impl/src/main/groovy/org/gradle/api/internal/artifacts/ivyservice/ivyresolve/strategy/LatestVersionMatcher.java
new file mode 100644
index 0000000..490c7ed
--- /dev/null
+++ b/subprojects/core-impl/src/main/groovy/org/gradle/api/internal/artifacts/ivyservice/ivyresolve/strategy/LatestVersionMatcher.java
@@ -0,0 +1,47 @@
+/*
+ * Copyright 2013 the original author or authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY 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.ivyresolve.strategy;
+
+import org.gradle.api.internal.artifacts.ivyservice.ivyresolve.ModuleVersionMetaData;
+
+public class LatestVersionMatcher implements VersionMatcher {
+    public boolean canHandle(String selector) {
+        return selector.startsWith("latest.");
+    }
+
+    public boolean isDynamic(String selector) {
+        return true;
+    }
+
+    public boolean needModuleMetadata(String selector, String candidate) {
+        return true;
+    }
+
+    public boolean accept(String selector, String candidate) {
+        throw new UnsupportedOperationException("accept(String, String)");
+    }
+
+    public boolean accept(String selector, ModuleVersionMetaData candidate) {
+        String selectorStatus = selector.substring("latest.".length());
+        int selectorStatusIndex = candidate.getStatusScheme().indexOf(selectorStatus);
+        int candidateStatusIndex = candidate.getStatusScheme().indexOf(candidate.getStatus());
+        return selectorStatusIndex >=0 && selectorStatusIndex <= candidateStatusIndex;
+    }
+
+    public int compare(String selector, String candidate) {
+        return 0;
+    }
+}
diff --git a/subprojects/core-impl/src/main/groovy/org/gradle/api/internal/artifacts/ivyservice/ivyresolve/strategy/LatestVersionStrategy.java b/subprojects/core-impl/src/main/groovy/org/gradle/api/internal/artifacts/ivyservice/ivyresolve/strategy/LatestVersionStrategy.java
new file mode 100644
index 0000000..576d258
--- /dev/null
+++ b/subprojects/core-impl/src/main/groovy/org/gradle/api/internal/artifacts/ivyservice/ivyresolve/strategy/LatestVersionStrategy.java
@@ -0,0 +1,66 @@
+/*
+ * Copyright 2013 the original author or authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY 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.ivyresolve.strategy;
+
+import com.google.common.collect.Lists;
+
+import org.gradle.api.internal.artifacts.ivyservice.ivyresolve.Versioned;
+
+import java.util.*;
+
+class LatestVersionStrategy implements LatestStrategy {
+    private final VersionMatcher versionMatcher;
+
+    public LatestVersionStrategy(VersionMatcher versionMatcher) {
+        this.versionMatcher = versionMatcher;
+    }
+
+    public <T extends Versioned> List<T> sort(Collection<T> versions) {
+        List<T> result = Lists.newArrayList(versions);
+        Collections.sort(result, this);
+        return result;
+    }
+
+    public <T extends Versioned> T findLatest(Collection<T> elements) {
+        return Collections.max(elements, this);
+    }
+
+    // TODO: Compared to Ivy's LatestRevisionStrategy.compare(), this method doesn't do any
+    // equivalent of ModuleRevisionId.normalizeRevision(). Should we add this functionality
+    // back in, either here or (probably better) in (Chain)VersionMatcher?
+    public int compare(Versioned element1, Versioned element2) {
+        String version1 = element1.getVersion();
+        String version2 = element2.getVersion();
+
+        /*
+         * The revisions can still be not resolved, so we use the current version matcher to
+         * know if one revision is dynamic, and in this case if it should be considered greater
+         * or lower than the other one. Note that if the version matcher compare method returns
+         * 0, it's because it's not possible to know which revision is greater. In this case we
+         * consider the dynamic one to be greater, because most of the time it will then be
+         * actually resolved and a real comparison will occur.
+         */
+        if (versionMatcher.isDynamic(version1)) {
+            int c = versionMatcher.compare(version1, version2);
+            return c >= 0 ? 1 : -1;
+        } else if (versionMatcher.isDynamic(version2)) {
+            int c = versionMatcher.compare(version2, version1);
+            return c >= 0 ? -1 : 1;
+        }
+
+        return versionMatcher.compare(version1, version2);
+    }
+}
diff --git a/subprojects/core-impl/src/main/groovy/org/gradle/api/internal/artifacts/ivyservice/ivyresolve/strategy/ResolverStrategy.java b/subprojects/core-impl/src/main/groovy/org/gradle/api/internal/artifacts/ivyservice/ivyresolve/strategy/ResolverStrategy.java
new file mode 100644
index 0000000..db0a295
--- /dev/null
+++ b/subprojects/core-impl/src/main/groovy/org/gradle/api/internal/artifacts/ivyservice/ivyresolve/strategy/ResolverStrategy.java
@@ -0,0 +1,62 @@
+/*
+ * Copyright 2013 the original author or authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY 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.ivyresolve.strategy;
+
+import com.google.common.collect.Maps;
+import org.apache.ivy.plugins.matcher.*;
+
+import java.util.Map;
+
+public class ResolverStrategy {
+    public static final ResolverStrategy INSTANCE = new ResolverStrategy();
+
+    private final VersionMatcher versionMatcher;
+    private final LatestStrategy latestStrategy;
+    private final Map<String, PatternMatcher> matchers = Maps.newHashMap();
+
+    private ResolverStrategy() {
+        ChainVersionMatcher chain = new ChainVersionMatcher();
+        chain.add(new VersionRangeMatcher(new ExactVersionMatcher()));
+        chain.add(new SubVersionMatcher(new ExactVersionMatcher()));
+        chain.add(new LatestVersionMatcher());
+        chain.add(new ExactVersionMatcher());
+
+        versionMatcher = chain;
+        latestStrategy = new LatestVersionStrategy(chain);
+
+        addMatcher(ExactPatternMatcher.INSTANCE);
+        addMatcher(RegexpPatternMatcher.INSTANCE);
+        addMatcher(ExactOrRegexpPatternMatcher.INSTANCE);
+        addMatcher(GlobPatternMatcher.INSTANCE);
+    }
+
+    private void addMatcher(PatternMatcher instance) {
+        matchers.put(instance.getName(), instance);
+    }
+
+    public VersionMatcher getVersionMatcher() {
+        return versionMatcher;
+    }
+
+    public LatestStrategy getLatestStrategy() {
+        return latestStrategy;
+    }
+
+    public PatternMatcher getPatternMatcher(String name) {
+        return matchers.get(name);
+    }
+}
diff --git a/subprojects/core-impl/src/main/groovy/org/gradle/api/internal/artifacts/ivyservice/ivyresolve/strategy/SubVersionMatcher.java b/subprojects/core-impl/src/main/groovy/org/gradle/api/internal/artifacts/ivyservice/ivyresolve/strategy/SubVersionMatcher.java
new file mode 100644
index 0000000..f877fa4
--- /dev/null
+++ b/subprojects/core-impl/src/main/groovy/org/gradle/api/internal/artifacts/ivyservice/ivyresolve/strategy/SubVersionMatcher.java
@@ -0,0 +1,59 @@
+/*
+ * Copyright 2013 the original author or authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY 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.ivyresolve.strategy;
+
+import org.gradle.api.internal.artifacts.ivyservice.ivyresolve.ModuleVersionMetaData;
+
+import java.util.Comparator;
+
+/**
+ * Version matcher for dynamic version selectors ending in '+'.
+ */
+public class SubVersionMatcher implements VersionMatcher {
+    private final Comparator<String> staticVersionComparator;
+
+    public SubVersionMatcher(VersionMatcher staticVersionComparator) {
+        this.staticVersionComparator = staticVersionComparator;
+    }
+
+    public boolean canHandle(String selector) {
+        return selector.endsWith("+");
+    }
+
+    public boolean isDynamic(String selector) {
+        return true;
+    }
+
+    public boolean needModuleMetadata(String selector, String candidate) {
+        return false;
+    }
+
+    public boolean accept(String selector, String candidate) {
+        String prefix = selector.substring(0, selector.length() - 1);
+        return candidate.startsWith(prefix);
+    }
+
+    public boolean accept(String selector, ModuleVersionMetaData candidate) {
+        return accept(selector, candidate.getId().getVersion());
+    }
+
+    public int compare(String selector, String candidate) {
+        if (accept(selector, candidate)) {
+            return 1;
+        }
+        return staticVersionComparator.compare(selector, candidate);
+    }
+}
diff --git a/subprojects/core-impl/src/main/groovy/org/gradle/api/internal/artifacts/ivyservice/ivyresolve/strategy/VersionMatcher.java b/subprojects/core-impl/src/main/groovy/org/gradle/api/internal/artifacts/ivyservice/ivyresolve/strategy/VersionMatcher.java
new file mode 100644
index 0000000..ce92b58
--- /dev/null
+++ b/subprojects/core-impl/src/main/groovy/org/gradle/api/internal/artifacts/ivyservice/ivyresolve/strategy/VersionMatcher.java
@@ -0,0 +1,68 @@
+/*
+ * Copyright 2013 the original author or authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY 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.ivyresolve.strategy;
+
+import org.gradle.api.internal.artifacts.ivyservice.ivyresolve.ModuleVersionMetaData;
+
+import java.util.Comparator;
+
+/**
+ * Compares version selectors against candidate versions, indicating whether they match or not.
+ *
+ * <p>This interface was initially derived from {@code org.apache.ivy.plugins.version.VersionMatcher}.
+ */
+public interface VersionMatcher extends Comparator<String> {
+    /**
+     * Tells if this version matcher can handle the given version selector. If {@code false}
+     * is returned, none of the other methods will be called for the given selector.
+     */
+    public boolean canHandle(String selector);
+
+    /**
+     * Indicates if the given version selector is dynamic.
+     */
+    public boolean isDynamic(String selector);
+
+    /**
+     * Indicates if module metadata is required to determine if the given version
+     * selector matches the given candidate version.
+     */
+    public boolean needModuleMetadata(String selector, String candidate);
+
+    /**
+     * Indicates if the given version selector matches the given candidate version.
+     * Only called if {@link #needModuleMetadata} returned {@code false} for the given selector
+     * and candidate version.
+     */
+    public boolean accept(String selector, String candidate);
+
+    /**
+     * Indicates if the given version selector matches the given given candidate version
+     * (whose metadata is provided). May also be called if {@link #isDynamic} returned
+     * {@code false} for the given selector, in which case it should return the same result as
+     * {@code accept(selector, candidate.getId().getVersion()}.
+     */
+    public boolean accept(String selector, ModuleVersionMetaData candidate);
+
+    /**
+     *
+     * Compares a version selector with a candidate version to indicate which is greater. If there is
+     * not enough information to tell which is greater, the version selector should be considered greater
+     * and this method should return 0.
+     */
+    public int compare(String selector, String candidate);
+}
\ No newline at end of file
diff --git a/subprojects/core-impl/src/main/groovy/org/gradle/api/internal/artifacts/ivyservice/ivyresolve/strategy/VersionRangeMatcher.java b/subprojects/core-impl/src/main/groovy/org/gradle/api/internal/artifacts/ivyservice/ivyresolve/strategy/VersionRangeMatcher.java
new file mode 100644
index 0000000..96182ad
--- /dev/null
+++ b/subprojects/core-impl/src/main/groovy/org/gradle/api/internal/artifacts/ivyservice/ivyresolve/strategy/VersionRangeMatcher.java
@@ -0,0 +1,180 @@
+/*
+ * Copyright 2013 the original author or authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY 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.ivyresolve.strategy;
+
+import org.gradle.api.internal.artifacts.ivyservice.ivyresolve.ModuleVersionMetaData;
+
+import java.util.Comparator;
+import java.util.regex.Matcher;
+import java.util.regex.Pattern;
+
+/**
+ * Matches version ranges: [1.0,2.0] matches all versions greater or equal to 1.0 and lower or equal
+ * to 2.0 [1.0,2.0[ matches all versions greater or equal to 1.0 and lower than 2.0 ]1.0,2.0]
+ * matches all versions greater than 1.0 and lower or equal to 2.0 ]1.0,2.0[ matches all versions
+ * greater than 1.0 and lower than 2.0 [1.0,) matches all versions greater or equal to 1.0 ]1.0,)
+ * matches all versions greater than 1.0 (,2.0] matches all versions lower or equal to 2.0 (,2.0[
+ * matches all versions lower than 2.0 This class uses a latest strategy to compare revisions. If
+ * none is set, it uses the default one of the ivy instance set through setIvy(). If neither a
+ * latest strategy nor a ivy instance is set, an IllegalStateException will be thrown when calling
+ * accept(). Note that it can't work with latest time strategy, cause no time is known for the
+ * limits of the range. Therefore only purely revision based LatestStrategy can be used.
+ */
+class VersionRangeMatcher implements VersionMatcher {
+    private static final String OPEN_INC = "[";
+
+    private static final String OPEN_EXC = "]";
+    private static final String OPEN_EXC_MAVEN = "(";
+
+    private static final String CLOSE_INC = "]";
+
+    private static final String CLOSE_EXC = "[";
+    private static final String CLOSE_EXC_MAVEN = ")";
+
+    private static final String LOWER_INFINITE = "(";
+
+    private static final String UPPER_INFINITE = ")";
+
+    private static final String SEPARATOR = ",";
+
+    // following patterns are built upon constants above and should not be modified
+    private static final String OPEN_INC_PATTERN = "\\" + OPEN_INC;
+
+    private static final String OPEN_EXC_PATTERN = "\\" + OPEN_EXC + "\\" + OPEN_EXC_MAVEN;
+
+    private static final String CLOSE_INC_PATTERN = "\\" + CLOSE_INC;
+
+    private static final String CLOSE_EXC_PATTERN = "\\" + CLOSE_EXC + "\\" + CLOSE_EXC_MAVEN;
+
+    private static final String LI_PATTERN = "\\" + LOWER_INFINITE;
+
+    private static final String UI_PATTERN = "\\" + UPPER_INFINITE;
+
+    private static final String SEP_PATTERN = "\\s*\\" + SEPARATOR + "\\s*";
+
+    private static final String OPEN_PATTERN = "[" + OPEN_INC_PATTERN + OPEN_EXC_PATTERN + "]";
+
+    private static final String CLOSE_PATTERN = "[" + CLOSE_INC_PATTERN + CLOSE_EXC_PATTERN + "]";
+
+    private static final String ANY_NON_SPECIAL_PATTERN = "[^\\s" + SEPARATOR + OPEN_INC_PATTERN
+            + OPEN_EXC_PATTERN + CLOSE_INC_PATTERN + CLOSE_EXC_PATTERN + LI_PATTERN + UI_PATTERN
+            + "]";
+
+    private static final String FINITE_PATTERN = OPEN_PATTERN + "\\s*(" + ANY_NON_SPECIAL_PATTERN
+            + "+)" + SEP_PATTERN + "(" + ANY_NON_SPECIAL_PATTERN + "+)\\s*" + CLOSE_PATTERN;
+
+    private static final String LOWER_INFINITE_PATTERN = LI_PATTERN + SEP_PATTERN + "("
+            + ANY_NON_SPECIAL_PATTERN + "+)\\s*" + CLOSE_PATTERN;
+
+    private static final String UPPER_INFINITE_PATTERN = OPEN_PATTERN + "\\s*("
+            + ANY_NON_SPECIAL_PATTERN + "+)" + SEP_PATTERN + UI_PATTERN;
+
+    private static final Pattern FINITE_RANGE = Pattern.compile(FINITE_PATTERN);
+
+    private static final Pattern LOWER_INFINITE_RANGE = Pattern.compile(LOWER_INFINITE_PATTERN);
+
+    private static final Pattern UPPER_INFINITE_RANGE = Pattern.compile(UPPER_INFINITE_PATTERN);
+
+    private static final Pattern ALL_RANGE = Pattern.compile(FINITE_PATTERN + "|"
+            + LOWER_INFINITE_PATTERN + "|" + UPPER_INFINITE_PATTERN);
+
+    private final Comparator<String> staticVersionComparator;
+
+    public VersionRangeMatcher(VersionMatcher staticVersionComparator) {
+        this.staticVersionComparator = staticVersionComparator;
+    }
+
+    public boolean canHandle(String selector) {
+        return ALL_RANGE.matcher(selector).matches();
+    }
+
+    public boolean isDynamic(String selector) {
+        return true;
+    }
+
+    public boolean needModuleMetadata(String selector, String candidate) {
+        return false;
+    }
+
+    public boolean accept(String selector, String candidate) {
+        Matcher matcher;
+        matcher = FINITE_RANGE.matcher(selector);
+        if (matcher.matches()) {
+            String lower = matcher.group(1);
+            String upper = matcher.group(2);
+            return isHigher(candidate, lower, selector.startsWith(OPEN_INC))
+                    && isLower(candidate, upper, selector.endsWith(CLOSE_INC));
+        }
+        matcher = LOWER_INFINITE_RANGE.matcher(selector);
+        if (matcher.matches()) {
+            String upper = matcher.group(1);
+            return isLower(candidate, upper, selector.endsWith(CLOSE_INC));
+        }
+        matcher = UPPER_INFINITE_RANGE.matcher(selector);
+        if (matcher.matches()) {
+            String lower = matcher.group(1);
+            return isHigher(candidate, lower, selector.startsWith(OPEN_INC));
+        }
+        throw new IllegalArgumentException("Not a version range selector: " + selector);
+    }
+
+    public boolean accept(String selector, ModuleVersionMetaData candidate) {
+        return accept(selector, candidate.getId().getVersion());
+    }
+
+    // doesn't seem to be quite in sync with accept() (e.g. open/close is not distinguished here)
+    public int compare(String selector, String candidate) {
+        Matcher m;
+        m = UPPER_INFINITE_RANGE.matcher(selector);
+        if (m.matches()) {
+            // no upper limit, the selector can always be considered greater
+            return 1;
+        }
+        String upper;
+        m = FINITE_RANGE.matcher(selector);
+        if (m.matches()) {
+            upper = m.group(2);
+        } else {
+            m = LOWER_INFINITE_RANGE.matcher(selector);
+            if (m.matches()) {
+                upper = m.group(1);
+            } else {
+                throw new IllegalArgumentException("Not a version range selector: " + selector);
+            }
+        }
+        int c = staticVersionComparator.compare(upper, candidate);
+        // If the comparison considers them equal, we must return -1, because we can't consider the
+        // dynamic version selector to be greater. Otherwise we can safely return the result of the static
+        // comparison.
+        return c == 0 ? -1 : c;
+    }
+
+    /**
+     * Tells if version1 is lower than version2.
+     */
+    private boolean isLower(String version1, String version2, boolean inclusive) {
+        int result = staticVersionComparator.compare(version1, version2);
+        return result <= (inclusive ? 0 : -1);
+    }
+
+    /**
+     * Tells if version1 is higher than version2.
+     */
+    private boolean isHigher(String version1, String version2, boolean inclusive) {
+        int result = staticVersionComparator.compare(version1, version2);
+        return result >= (inclusive ? 0 : 1);
+    }
+}
diff --git a/subprojects/core-impl/src/main/groovy/org/gradle/api/internal/artifacts/ivyservice/modulecache/CachedModuleDescriptorParseContext.java b/subprojects/core-impl/src/main/groovy/org/gradle/api/internal/artifacts/ivyservice/modulecache/CachedModuleDescriptorParseContext.java
new file mode 100644
index 0000000..e91c0f2
--- /dev/null
+++ b/subprojects/core-impl/src/main/groovy/org/gradle/api/internal/artifacts/ivyservice/modulecache/CachedModuleDescriptorParseContext.java
@@ -0,0 +1,53 @@
+/*
+ * Copyright 2013 the original author or authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY 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.modulecache;
+
+import org.apache.ivy.core.module.descriptor.Artifact;
+import org.apache.ivy.core.module.descriptor.DefaultDependencyDescriptor;
+import org.apache.ivy.core.module.descriptor.ModuleDescriptor;
+import org.apache.ivy.core.module.id.ModuleRevisionId;
+import org.gradle.api.internal.artifacts.ivyservice.DefaultBuildableModuleVersionResolveResult;
+import org.gradle.api.internal.artifacts.ivyservice.DependencyToModuleVersionResolver;
+import org.gradle.api.internal.artifacts.ivyservice.ivyresolve.DefaultDependencyMetaData;
+import org.gradle.api.internal.artifacts.ivyservice.ivyresolve.parser.AbstractDescriptorParseContext;
+
+/**
+ * Context used for parsing cached module descriptor files.
+ * Will only be used for parsing ivy.xml files, as pom files are converted before caching.
+ */
+class CachedModuleDescriptorParseContext extends AbstractDescriptorParseContext {
+    private final DependencyToModuleVersionResolver resolver;
+
+    public CachedModuleDescriptorParseContext(DependencyToModuleVersionResolver resolver, String defaultStatus) {
+        super(defaultStatus);
+        this.resolver = resolver;
+    }
+
+    public ModuleRevisionId getCurrentRevisionId() {
+        throw new UnsupportedOperationException();
+    }
+
+    public boolean artifactExists(Artifact artifact) {
+        throw new UnsupportedOperationException();
+    }
+
+    public ModuleDescriptor getModuleDescriptor(ModuleRevisionId moduleRevisionId) {
+        DefaultBuildableModuleVersionResolveResult result = new DefaultBuildableModuleVersionResolveResult();
+        resolver.resolve(new DefaultDependencyMetaData(new DefaultDependencyDescriptor(moduleRevisionId, true)), result);
+        return result.getMetaData().getDescriptor();
+    }
+}
diff --git a/subprojects/core-impl/src/main/groovy/org/gradle/api/internal/artifacts/ivyservice/modulecache/DefaultCachedModuleDescriptor.java b/subprojects/core-impl/src/main/groovy/org/gradle/api/internal/artifacts/ivyservice/modulecache/DefaultCachedModuleDescriptor.java
index 6596c0e..6e4a7ae 100644
--- a/subprojects/core-impl/src/main/groovy/org/gradle/api/internal/artifacts/ivyservice/modulecache/DefaultCachedModuleDescriptor.java
+++ b/subprojects/core-impl/src/main/groovy/org/gradle/api/internal/artifacts/ivyservice/modulecache/DefaultCachedModuleDescriptor.java
@@ -16,8 +16,9 @@
 package org.gradle.api.internal.artifacts.ivyservice.modulecache;
 
 import org.apache.ivy.core.module.descriptor.ModuleDescriptor;
-import org.apache.ivy.core.module.id.ModuleRevisionId;
+import org.gradle.api.artifacts.ModuleVersionIdentifier;
 import org.gradle.api.artifacts.ResolvedModuleVersion;
+import org.gradle.api.internal.artifacts.DefaultModuleVersionIdentifier;
 import org.gradle.api.internal.artifacts.ivyservice.dynamicversions.DefaultResolvedModuleVersion;
 import org.gradle.api.internal.artifacts.ivyservice.ivyresolve.ModuleSource;
 import org.gradle.internal.TimeProvider;
@@ -48,8 +49,8 @@ class DefaultCachedModuleDescriptor implements ModuleDescriptorCache.CachedModul
     }
 
     public ResolvedModuleVersion getModuleVersion() {
-        ModuleRevisionId moduleRevisionId = isMissing() ? null : moduleDescriptor.getModuleRevisionId();
-        return new DefaultResolvedModuleVersion(moduleRevisionId);
+        ModuleVersionIdentifier moduleVersionIdentifier = isMissing() ? null : DefaultModuleVersionIdentifier.newId(moduleDescriptor.getModuleRevisionId());
+        return new DefaultResolvedModuleVersion(moduleVersionIdentifier);
     }
 
     public ModuleDescriptor getModuleDescriptor() {
diff --git a/subprojects/core-impl/src/main/groovy/org/gradle/api/internal/artifacts/ivyservice/modulecache/DefaultModuleDescriptorCache.java b/subprojects/core-impl/src/main/groovy/org/gradle/api/internal/artifacts/ivyservice/modulecache/DefaultModuleDescriptorCache.java
index 715c70c..0b663bc 100644
--- a/subprojects/core-impl/src/main/groovy/org/gradle/api/internal/artifacts/ivyservice/modulecache/DefaultModuleDescriptorCache.java
+++ b/subprojects/core-impl/src/main/groovy/org/gradle/api/internal/artifacts/ivyservice/modulecache/DefaultModuleDescriptorCache.java
@@ -20,16 +20,16 @@ import org.gradle.api.artifacts.ModuleVersionIdentifier;
 import org.gradle.api.internal.artifacts.ModuleVersionIdentifierSerializer;
 import org.gradle.api.internal.artifacts.ivyservice.ArtifactCacheMetaData;
 import org.gradle.api.internal.artifacts.ivyservice.CacheLockingManager;
+import org.gradle.api.internal.artifacts.ivyservice.DependencyToModuleVersionResolver;
 import org.gradle.api.internal.artifacts.ivyservice.IvyXmlModuleDescriptorWriter;
 import org.gradle.api.internal.artifacts.ivyservice.ivyresolve.ModuleSource;
 import org.gradle.api.internal.artifacts.ivyservice.ivyresolve.ModuleVersionRepository;
 import org.gradle.api.internal.artifacts.ivyservice.ivyresolve.parser.IvyXmlModuleDescriptorParser;
-import org.gradle.api.internal.filestore.FileStoreEntry;
 import org.gradle.api.internal.filestore.PathKeyFileStore;
 import org.gradle.cache.PersistentIndexedCache;
 import org.gradle.internal.TimeProvider;
-import org.gradle.messaging.serialize.DataStreamBackedSerializer;
-import org.gradle.messaging.serialize.DefaultSerializer;
+import org.gradle.internal.resource.local.LocallyAvailableResource;
+import org.gradle.messaging.serialize.*;
 import org.gradle.util.hash.HashValue;
 import org.slf4j.Logger;
 import org.slf4j.LoggerFactory;
@@ -67,7 +67,7 @@ public class DefaultModuleDescriptorCache implements ModuleDescriptorCache {
         return cacheLockingManager.createCache(artifactResolutionCacheFile, new RevisionKeySerializer(), new ModuleDescriptorCacheEntrySerializer());
     }
 
-    public CachedModuleDescriptor getCachedModuleDescriptor(ModuleVersionRepository repository, ModuleVersionIdentifier moduleVersionIdentifier) {
+    public CachedModuleDescriptor getCachedModuleDescriptor(ModuleVersionRepository repository, ModuleVersionIdentifier moduleVersionIdentifier, DependencyToModuleVersionResolver resolver) {
         ModuleDescriptorCacheEntry moduleDescriptorCacheEntry = getCache().get(createKey(repository, moduleVersionIdentifier));
         if (moduleDescriptorCacheEntry == null) {
             return null;
@@ -75,7 +75,7 @@ public class DefaultModuleDescriptorCache implements ModuleDescriptorCache {
         if (moduleDescriptorCacheEntry.isMissing) {
             return new DefaultCachedModuleDescriptor(moduleDescriptorCacheEntry, null, timeProvider);
         }
-        ModuleDescriptor descriptor = moduleDescriptorStore.getModuleDescriptor(repository, moduleVersionIdentifier);
+        ModuleDescriptor descriptor = moduleDescriptorStore.getModuleDescriptor(repository, moduleVersionIdentifier, resolver);
         if (descriptor == null) {
             // Descriptor file has been manually deleted - ignore the entry
             return null;
@@ -91,8 +91,8 @@ public class DefaultModuleDescriptorCache implements ModuleDescriptorCache {
             getCache().put(createKey(repository, moduleVersionIdentifier), entry);
         } else {
             LOGGER.debug("Recording module descriptor in cache: {} [changing = {}]", moduleDescriptor.getModuleRevisionId(), isChanging);
-            FileStoreEntry fileStoreEntry = moduleDescriptorStore.putModuleDescriptor(repository, moduleDescriptor);
-            entry = createEntry(isChanging, fileStoreEntry.getSha1(), moduleSource);
+            LocallyAvailableResource resource = moduleDescriptorStore.putModuleDescriptor(repository, moduleDescriptor);
+            entry = createEntry(isChanging, resource.getSha1(), moduleSource);
             getCache().put(createKey(repository, moduleVersionIdentifier), entry);
         }
         return new DefaultCachedModuleDescriptor(entry, null, timeProvider);
@@ -134,53 +134,45 @@ public class DefaultModuleDescriptorCache implements ModuleDescriptorCache {
         }
     }
 
-    private static class RevisionKeySerializer extends DataStreamBackedSerializer<RevisionKey> {
+    private static class RevisionKeySerializer implements Serializer<RevisionKey> {
         private final ModuleVersionIdentifierSerializer identifierSerializer = new ModuleVersionIdentifierSerializer();
 
-        @Override
-        public void write(DataOutput dataOutput, RevisionKey value) throws IOException {
-            dataOutput.writeUTF(value.repositoryId);
-            identifierSerializer.write(dataOutput, value.moduleVersionIdentifier);
+        public void write(Encoder encoder, RevisionKey value) throws Exception {
+            encoder.writeString(value.repositoryId);
+            identifierSerializer.write(encoder, value.moduleVersionIdentifier);
         }
 
-        @Override
-        public RevisionKey read(DataInput dataInput) throws IOException {
-            String resolverId = dataInput.readUTF();
-            ModuleVersionIdentifier identifier = identifierSerializer.read(dataInput);
+        public RevisionKey read(Decoder decoder) throws Exception {
+            String resolverId = decoder.readString();
+            ModuleVersionIdentifier identifier = identifierSerializer.read(decoder);
             return new RevisionKey(resolverId, identifier);
         }
     }
 
-    private static class ModuleDescriptorCacheEntrySerializer extends DataStreamBackedSerializer<ModuleDescriptorCacheEntry> {
+    private static class ModuleDescriptorCacheEntrySerializer implements Serializer<ModuleDescriptorCacheEntry> {
         private final DefaultSerializer<ModuleSource> moduleSourceSerializer = new DefaultSerializer<ModuleSource>(ModuleSource.class.getClassLoader());
 
-        @Override
-        public void write(DataOutput dataOutput, ModuleDescriptorCacheEntry value) throws IOException {
-            dataOutput.writeBoolean(value.isMissing);
-            dataOutput.writeBoolean(value.isChanging);
-            dataOutput.writeLong(value.createTimestamp);
+        public void write(Encoder encoder, ModuleDescriptorCacheEntry value) throws Exception {
+            encoder.writeBoolean(value.isMissing);
+            encoder.writeBoolean(value.isChanging);
+            encoder.writeLong(value.createTimestamp);
             ByteArrayOutputStream outputStream = new ByteArrayOutputStream();
-            moduleSourceSerializer.write(outputStream, value.moduleSource);
+            OutputStreamBackedEncoder sourceEncoder = new OutputStreamBackedEncoder(outputStream);
+            moduleSourceSerializer.write(sourceEncoder, value.moduleSource);
+            sourceEncoder.flush();
             byte[] serializedModuleSource = outputStream.toByteArray();
-            dataOutput.writeInt(serializedModuleSource.length);
-            dataOutput.write(serializedModuleSource);
+            encoder.writeBinary(serializedModuleSource);
             byte[] hash = value.moduleDescriptorHash.toByteArray();
-            dataOutput.writeInt(hash.length);
-            dataOutput.write(hash);
+            encoder.writeBinary(hash);
         }
 
-        @Override
-        public ModuleDescriptorCacheEntry read(DataInput dataInput) throws Exception {
-            boolean isMissing = dataInput.readBoolean();
-            boolean isChanging = dataInput.readBoolean();
-            long createTimestamp = dataInput.readLong();
-            int count = dataInput.readInt();
-            byte[] serializedModuleSource = new byte[count];
-            dataInput.readFully(serializedModuleSource);
-            ModuleSource moduleSource = moduleSourceSerializer.read(new ByteArrayInputStream(serializedModuleSource));
-            count = dataInput.readInt();
-            byte[] encodedHash = new byte[count];
-            dataInput.readFully(encodedHash);
+        public ModuleDescriptorCacheEntry read(Decoder decoder) throws Exception {
+            boolean isMissing = decoder.readBoolean();
+            boolean isChanging = decoder.readBoolean();
+            long createTimestamp = decoder.readLong();
+            byte[] serializedModuleSource = decoder.readBinary();
+            ModuleSource moduleSource = moduleSourceSerializer.read(new InputStreamBackedDecoder(new ByteArrayInputStream(serializedModuleSource)));
+            byte[] encodedHash = decoder.readBinary();
             BigInteger hash = new BigInteger(encodedHash);
             return new ModuleDescriptorCacheEntry(isChanging, isMissing, createTimestamp, hash, moduleSource);
         }
diff --git a/subprojects/core-impl/src/main/groovy/org/gradle/api/internal/artifacts/ivyservice/modulecache/ModuleDescriptorCache.java b/subprojects/core-impl/src/main/groovy/org/gradle/api/internal/artifacts/ivyservice/modulecache/ModuleDescriptorCache.java
index 096535f..ec3427e 100644
--- a/subprojects/core-impl/src/main/groovy/org/gradle/api/internal/artifacts/ivyservice/modulecache/ModuleDescriptorCache.java
+++ b/subprojects/core-impl/src/main/groovy/org/gradle/api/internal/artifacts/ivyservice/modulecache/ModuleDescriptorCache.java
@@ -18,6 +18,7 @@ package org.gradle.api.internal.artifacts.ivyservice.modulecache;
 import org.apache.ivy.core.module.descriptor.ModuleDescriptor;
 import org.gradle.api.artifacts.ModuleVersionIdentifier;
 import org.gradle.api.artifacts.ResolvedModuleVersion;
+import org.gradle.api.internal.artifacts.ivyservice.DependencyToModuleVersionResolver;
 import org.gradle.api.internal.artifacts.ivyservice.ivyresolve.ModuleSource;
 import org.gradle.api.internal.artifacts.ivyservice.ivyresolve.ModuleVersionRepository;
 
@@ -26,7 +27,8 @@ import java.math.BigInteger;
 public interface ModuleDescriptorCache {
     CachedModuleDescriptor cacheModuleDescriptor(ModuleVersionRepository repository, ModuleVersionIdentifier resolvedModuleVersionIdentifier, ModuleDescriptor moduleDescriptor, ModuleSource moduleSource, boolean isChanging);
 
-    CachedModuleDescriptor getCachedModuleDescriptor(ModuleVersionRepository repository, ModuleVersionIdentifier moduleVersionIdentifier);
+    // TODO:DAZ Should not need a resolver to parse the cached descriptors: descriptor should be self-contained.
+    CachedModuleDescriptor getCachedModuleDescriptor(ModuleVersionRepository repository, ModuleVersionIdentifier moduleVersionIdentifier, DependencyToModuleVersionResolver resolver);
 
     interface CachedModuleDescriptor {
         ResolvedModuleVersion getModuleVersion();
diff --git a/subprojects/core-impl/src/main/groovy/org/gradle/api/internal/artifacts/ivyservice/modulecache/ModuleDescriptorStore.java b/subprojects/core-impl/src/main/groovy/org/gradle/api/internal/artifacts/ivyservice/modulecache/ModuleDescriptorStore.java
index 03bf319..c18643a 100644
--- a/subprojects/core-impl/src/main/groovy/org/gradle/api/internal/artifacts/ivyservice/modulecache/ModuleDescriptorStore.java
+++ b/subprojects/core-impl/src/main/groovy/org/gradle/api/internal/artifacts/ivyservice/modulecache/ModuleDescriptorStore.java
@@ -17,43 +17,43 @@ package org.gradle.api.internal.artifacts.ivyservice.modulecache;
 
 import org.apache.ivy.core.module.descriptor.ModuleDescriptor;
 import org.apache.ivy.core.module.id.ModuleRevisionId;
-import org.apache.ivy.plugins.parser.ParserSettings;
 import org.gradle.api.Action;
 import org.gradle.api.artifacts.ModuleVersionIdentifier;
+import org.gradle.api.internal.artifacts.ivyservice.DependencyToModuleVersionResolver;
 import org.gradle.api.internal.artifacts.ivyservice.IvyModuleDescriptorWriter;
-import org.gradle.api.internal.artifacts.ivyservice.ivyresolve.IvyContextualiser;
 import org.gradle.api.internal.artifacts.ivyservice.ivyresolve.ModuleVersionRepository;
-import org.gradle.api.internal.artifacts.ivyservice.ivyresolve.parser.IvyXmlModuleDescriptorParser;
-import org.gradle.api.internal.filestore.FileStoreEntry;
+import org.gradle.api.internal.artifacts.ivyservice.ivyresolve.parser.DescriptorParseContext;
+import org.gradle.api.internal.artifacts.ivyservice.ivyresolve.parser.ModuleDescriptorParser;
 import org.gradle.api.internal.filestore.PathKeyFileStore;
 import org.gradle.internal.UncheckedException;
+import org.gradle.internal.resource.local.LocallyAvailableResource;
 
 import java.io.File;
-import java.net.URL;
 
 public class ModuleDescriptorStore {
 
     public static final String FILE_PATH_PATTERN = "module-metadata/%s/%s/%s/%s/ivy.xml";
-    private final IvyXmlModuleDescriptorParser parser;
+    private final ModuleDescriptorParser parser;
     private final PathKeyFileStore pathKeyFileStore;
     private final IvyModuleDescriptorWriter ivyModuleDescriptorWriter;
 
-    public ModuleDescriptorStore(PathKeyFileStore pathKeyFileStore, IvyModuleDescriptorWriter ivyModuleDescriptorWriter, IvyXmlModuleDescriptorParser ivyXmlModuleDescriptorParser) {
+    public ModuleDescriptorStore(PathKeyFileStore pathKeyFileStore, IvyModuleDescriptorWriter ivyModuleDescriptorWriter, ModuleDescriptorParser ivyXmlModuleDescriptorParser) {
         this.pathKeyFileStore = pathKeyFileStore;
         this.ivyModuleDescriptorWriter = ivyModuleDescriptorWriter;
         parser = ivyXmlModuleDescriptorParser;
     }
 
-    public ModuleDescriptor getModuleDescriptor(ModuleVersionRepository repository, ModuleVersionIdentifier moduleVersionIdentifier) {
+    // TODO:DAZ Persisted module descriptors should be self-contained: should not need to resolve included descriptors
+    public ModuleDescriptor getModuleDescriptor(ModuleVersionRepository repository, ModuleVersionIdentifier moduleVersionIdentifier, DependencyToModuleVersionResolver resolver) {
         String filePath = getFilePath(repository, moduleVersionIdentifier);
-        final FileStoreEntry fileStoreEntry = pathKeyFileStore.get(filePath);
-        if (fileStoreEntry != null) {
-            return parseModuleDescriptorFile(fileStoreEntry.getFile());
+        final LocallyAvailableResource resource = pathKeyFileStore.get(filePath);
+        if (resource != null) {
+            return parseModuleDescriptorFile(resource.getFile(), resolver);
         }
         return null;
     }
 
-    public FileStoreEntry putModuleDescriptor(ModuleVersionRepository repository, final ModuleDescriptor moduleDescriptor) {
+    public LocallyAvailableResource putModuleDescriptor(ModuleVersionRepository repository, final ModuleDescriptor moduleDescriptor) {
         String filePath = getFilePath(repository, moduleDescriptor.getModuleRevisionId());
         return pathKeyFileStore.add(filePath, new Action<File>() {
             public void execute(File moduleDescriptorFile) {
@@ -66,14 +66,9 @@ public class ModuleDescriptorStore {
         });
     }
 
-    private ModuleDescriptor parseModuleDescriptorFile(File moduleDescriptorFile) {
-        ParserSettings settings = IvyContextualiser.getIvyContext().getSettings();
-        try {
-            URL result = moduleDescriptorFile.toURI().toURL();
-            return parser.parseDescriptor(settings, result, false);
-        } catch (Exception e) {
-            throw UncheckedException.throwAsUncheckedException(e);
-        }
+    private ModuleDescriptor parseModuleDescriptorFile(File moduleDescriptorFile, DependencyToModuleVersionResolver resolver) {
+        DescriptorParseContext parserSettings = new CachedModuleDescriptorParseContext(resolver, "integration");
+        return parser.parseDescriptor(parserSettings, moduleDescriptorFile, false);
     }
 
     private String getFilePath(ModuleVersionRepository repository, ModuleRevisionId moduleRevisionId) {
diff --git a/subprojects/core-impl/src/main/groovy/org/gradle/api/internal/artifacts/ivyservice/moduleconverter/ArtifactsExtraAttributesStrategy.java b/subprojects/core-impl/src/main/groovy/org/gradle/api/internal/artifacts/ivyservice/moduleconverter/ArtifactsExtraAttributesStrategy.java
deleted file mode 100644
index 4828e51..0000000
--- a/subprojects/core-impl/src/main/groovy/org/gradle/api/internal/artifacts/ivyservice/moduleconverter/ArtifactsExtraAttributesStrategy.java
+++ /dev/null
@@ -1,27 +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.artifacts.ivyservice.moduleconverter;
-
-import org.gradle.api.artifacts.PublishArtifact;
-
-import java.util.Map;
-
-/**
- * @author Hans Dockter
- */
-public interface ArtifactsExtraAttributesStrategy {
-    Map<String, String> createExtraAttributes(PublishArtifact publishArtifact);
-}
diff --git a/subprojects/core-impl/src/main/groovy/org/gradle/api/internal/artifacts/ivyservice/moduleconverter/ArtifactsToModuleDescriptorConverter.java b/subprojects/core-impl/src/main/groovy/org/gradle/api/internal/artifacts/ivyservice/moduleconverter/ArtifactsToModuleDescriptorConverter.java
index 112e678..fe2e40f 100644
--- a/subprojects/core-impl/src/main/groovy/org/gradle/api/internal/artifacts/ivyservice/moduleconverter/ArtifactsToModuleDescriptorConverter.java
+++ b/subprojects/core-impl/src/main/groovy/org/gradle/api/internal/artifacts/ivyservice/moduleconverter/ArtifactsToModuleDescriptorConverter.java
@@ -15,12 +15,9 @@
  */
 package org.gradle.api.internal.artifacts.ivyservice.moduleconverter;
 
-import org.apache.ivy.core.module.descriptor.DefaultModuleDescriptor;
 import org.gradle.api.artifacts.Configuration;
+import org.gradle.api.internal.artifacts.BuildableModuleVersionPublishMetaData;
 
-/**
- * @author Hans Dockter
- */
 public interface ArtifactsToModuleDescriptorConverter {
-    void addArtifacts(DefaultModuleDescriptor moduleDescriptor, Iterable<? extends Configuration> configurations);
+    void addArtifacts(BuildableModuleVersionPublishMetaData metaData, Iterable<? extends Configuration> configurations);
 }
diff --git a/subprojects/core-impl/src/main/groovy/org/gradle/api/internal/artifacts/ivyservice/moduleconverter/ConfigurationsToModuleDescriptorConverter.java b/subprojects/core-impl/src/main/groovy/org/gradle/api/internal/artifacts/ivyservice/moduleconverter/ConfigurationsToModuleDescriptorConverter.java
index 4e033fa..2b82fe3 100644
--- a/subprojects/core-impl/src/main/groovy/org/gradle/api/internal/artifacts/ivyservice/moduleconverter/ConfigurationsToModuleDescriptorConverter.java
+++ b/subprojects/core-impl/src/main/groovy/org/gradle/api/internal/artifacts/ivyservice/moduleconverter/ConfigurationsToModuleDescriptorConverter.java
@@ -18,9 +18,6 @@ package org.gradle.api.internal.artifacts.ivyservice.moduleconverter;
 import org.apache.ivy.core.module.descriptor.DefaultModuleDescriptor;
 import org.gradle.api.artifacts.Configuration;
 
-/**
- * @author Hans Dockter
- */
 public interface ConfigurationsToModuleDescriptorConverter {
     void addConfigurations(DefaultModuleDescriptor moduleDescriptor, Iterable<? extends Configuration> configurations);
 }
diff --git a/subprojects/core-impl/src/main/groovy/org/gradle/api/internal/artifacts/ivyservice/moduleconverter/DefaultArtifactsToModuleDescriptorConverter.java b/subprojects/core-impl/src/main/groovy/org/gradle/api/internal/artifacts/ivyservice/moduleconverter/DefaultArtifactsToModuleDescriptorConverter.java
index 48000c1..a8e3f34 100644
--- a/subprojects/core-impl/src/main/groovy/org/gradle/api/internal/artifacts/ivyservice/moduleconverter/DefaultArtifactsToModuleDescriptorConverter.java
+++ b/subprojects/core-impl/src/main/groovy/org/gradle/api/internal/artifacts/ivyservice/moduleconverter/DefaultArtifactsToModuleDescriptorConverter.java
@@ -22,46 +22,27 @@ import org.apache.ivy.core.module.id.ModuleRevisionId;
 import org.gradle.api.artifacts.Configuration;
 import org.gradle.api.artifacts.Dependency;
 import org.gradle.api.artifacts.PublishArtifact;
-import org.gradle.api.internal.artifacts.ivyservice.DefaultIvyDependencyPublisher;
+import org.gradle.api.internal.artifacts.BuildableModuleVersionPublishMetaData;
 import org.gradle.util.GUtil;
-import org.gradle.util.WrapUtil;
 
 import java.util.HashMap;
 import java.util.Map;
 
-/**
- * @author Hans Dockter
- */
 public class DefaultArtifactsToModuleDescriptorConverter implements ArtifactsToModuleDescriptorConverter {
-    public final static ArtifactsExtraAttributesStrategy IVY_FILE_STRATEGY = new ArtifactsExtraAttributesStrategy() {
-        public Map<String, String> createExtraAttributes(PublishArtifact publishArtifact) {
-            return new HashMap<String, String>();
-        }
-    };
-
-    public final static ArtifactsExtraAttributesStrategy RESOLVE_STRATEGY = new ArtifactsExtraAttributesStrategy() {
-        public Map<String, String> createExtraAttributes(PublishArtifact publishArtifact) {
-            return WrapUtil.toMap(DefaultIvyDependencyPublisher.FILE_ABSOLUTE_PATH_EXTRA_ATTRIBUTE, publishArtifact.getFile().getAbsolutePath());
-        }
-    };
-
-    private ArtifactsExtraAttributesStrategy artifactsExtraAttributesStrategy;
-
-    public DefaultArtifactsToModuleDescriptorConverter(ArtifactsExtraAttributesStrategy artifactsExtraAttributesStrategy) {
-        this.artifactsExtraAttributesStrategy = artifactsExtraAttributesStrategy;
-    }
 
-    public void addArtifacts(DefaultModuleDescriptor moduleDescriptor, Iterable<? extends Configuration> configurations) {
+    public void addArtifacts(BuildableModuleVersionPublishMetaData metaData, Iterable<? extends Configuration> configurations) {
+        DefaultModuleDescriptor moduleDescriptor = metaData.getModuleDescriptor();
         for (Configuration configuration : configurations) {
             for (PublishArtifact publishArtifact : configuration.getArtifacts()) {
                 Artifact ivyArtifact = createIvyArtifact(publishArtifact, moduleDescriptor.getModuleRevisionId());
                 moduleDescriptor.addArtifact(configuration.getName(), ivyArtifact);
+                metaData.addArtifact(ivyArtifact, publishArtifact.getFile());
             }
         }
     }
 
     public Artifact createIvyArtifact(PublishArtifact publishArtifact, ModuleRevisionId moduleRevisionId) {
-        Map<String, String> extraAttributes = artifactsExtraAttributesStrategy.createExtraAttributes(publishArtifact);
+        Map<String, String> extraAttributes = new HashMap<String, String>();
         if (GUtil.isTrue(publishArtifact.getClassifier())) {
             extraAttributes.put(Dependency.CLASSIFIER, publishArtifact.getClassifier());
         }
diff --git a/subprojects/core-impl/src/main/groovy/org/gradle/api/internal/artifacts/ivyservice/moduleconverter/DefaultConfigurationsToModuleDescriptorConverter.java b/subprojects/core-impl/src/main/groovy/org/gradle/api/internal/artifacts/ivyservice/moduleconverter/DefaultConfigurationsToModuleDescriptorConverter.java
index 47dce97..c7cdc2d 100644
--- a/subprojects/core-impl/src/main/groovy/org/gradle/api/internal/artifacts/ivyservice/moduleconverter/DefaultConfigurationsToModuleDescriptorConverter.java
+++ b/subprojects/core-impl/src/main/groovy/org/gradle/api/internal/artifacts/ivyservice/moduleconverter/DefaultConfigurationsToModuleDescriptorConverter.java
@@ -21,9 +21,6 @@ import org.gradle.api.internal.artifacts.configurations.Configurations;
 
 import java.util.Arrays;
 
-/**
- * @author Hans Dockter
- */
 public class DefaultConfigurationsToModuleDescriptorConverter implements ConfigurationsToModuleDescriptorConverter {
     public void addConfigurations(DefaultModuleDescriptor moduleDescriptor, Iterable<? extends Configuration> configurations) {
         for (Configuration configuration : configurations) {
diff --git a/subprojects/core-impl/src/main/groovy/org/gradle/api/internal/artifacts/ivyservice/moduleconverter/DefaultExcludeRuleConverter.java b/subprojects/core-impl/src/main/groovy/org/gradle/api/internal/artifacts/ivyservice/moduleconverter/DefaultExcludeRuleConverter.java
index 3b6392e..13090fd 100644
--- a/subprojects/core-impl/src/main/groovy/org/gradle/api/internal/artifacts/ivyservice/moduleconverter/DefaultExcludeRuleConverter.java
+++ b/subprojects/core-impl/src/main/groovy/org/gradle/api/internal/artifacts/ivyservice/moduleconverter/DefaultExcludeRuleConverter.java
@@ -23,9 +23,6 @@ import org.apache.ivy.plugins.matcher.PatternMatcher;
 import org.gradle.api.artifacts.ExcludeRule;
 import org.gradle.util.GUtil;
 
-/**
- * @author Hans Dockter
- */
 public class DefaultExcludeRuleConverter implements ExcludeRuleConverter {
     public DefaultExcludeRule createExcludeRule(String configurationName, ExcludeRule excludeRule) {
         String org = GUtil.elvis(excludeRule.getGroup(), PatternMatcher.ANY_EXPRESSION);
diff --git a/subprojects/core-impl/src/main/groovy/org/gradle/api/internal/artifacts/ivyservice/moduleconverter/DefaultModuleDescriptorFactory.java b/subprojects/core-impl/src/main/groovy/org/gradle/api/internal/artifacts/ivyservice/moduleconverter/DefaultModuleDescriptorFactory.java
index 3abe439..5c1ffe3 100644
--- a/subprojects/core-impl/src/main/groovy/org/gradle/api/internal/artifacts/ivyservice/moduleconverter/DefaultModuleDescriptorFactory.java
+++ b/subprojects/core-impl/src/main/groovy/org/gradle/api/internal/artifacts/ivyservice/moduleconverter/DefaultModuleDescriptorFactory.java
@@ -15,34 +15,12 @@
  */
 package org.gradle.api.internal.artifacts.ivyservice.moduleconverter;
 
-import org.apache.ivy.Ivy;
-import org.apache.ivy.core.IvyContext;
 import org.apache.ivy.core.module.descriptor.DefaultModuleDescriptor;
 import org.gradle.api.artifacts.Module;
-import org.gradle.api.internal.artifacts.ivyservice.IvyFactory;
 import org.gradle.api.internal.artifacts.ivyservice.IvyUtil;
-import org.gradle.api.internal.artifacts.ivyservice.SettingsConverter;
 
-/**
- * @author Hans Dockter
- */
 public class DefaultModuleDescriptorFactory implements ModuleDescriptorFactory {
-    private final IvyFactory ivyFactory;
-    private final SettingsConverter settingsConverter;
-
-    public DefaultModuleDescriptorFactory(IvyFactory ivyFactory, SettingsConverter settingsConverter) {
-        this.ivyFactory = ivyFactory;
-        this.settingsConverter = settingsConverter;
-    }
-
     public DefaultModuleDescriptor createModuleDescriptor(Module module) {
-        IvyContext ivyContext = IvyContext.pushNewContext();
-        try {
-            Ivy ivy = ivyFactory.createIvy(settingsConverter.getForResolve());
-            ivyContext.setIvy(ivy);
-            return new DefaultModuleDescriptor(IvyUtil.createModuleRevisionId(module), module.getStatus(), null);
-        } finally {
-            IvyContext.popContext();
-        }
+        return new DefaultModuleDescriptor(IvyUtil.createModuleRevisionId(module), module.getStatus(), null);
     }
 }
diff --git a/subprojects/core-impl/src/main/groovy/org/gradle/api/internal/artifacts/ivyservice/moduleconverter/ExcludeRuleConverter.java b/subprojects/core-impl/src/main/groovy/org/gradle/api/internal/artifacts/ivyservice/moduleconverter/ExcludeRuleConverter.java
index 4ed4bc5..00c5612 100644
--- a/subprojects/core-impl/src/main/groovy/org/gradle/api/internal/artifacts/ivyservice/moduleconverter/ExcludeRuleConverter.java
+++ b/subprojects/core-impl/src/main/groovy/org/gradle/api/internal/artifacts/ivyservice/moduleconverter/ExcludeRuleConverter.java
@@ -17,9 +17,6 @@ package org.gradle.api.internal.artifacts.ivyservice.moduleconverter;
 
 import org.apache.ivy.core.module.descriptor.ExcludeRule;
 
-/**
- * @author Hans Dockter
- */
 public interface ExcludeRuleConverter {
     ExcludeRule createExcludeRule(String configuration, org.gradle.api.artifacts.ExcludeRule excludeRule);
 }
diff --git a/subprojects/core-impl/src/main/groovy/org/gradle/api/internal/artifacts/ivyservice/moduleconverter/ModuleDescriptorFactory.java b/subprojects/core-impl/src/main/groovy/org/gradle/api/internal/artifacts/ivyservice/moduleconverter/ModuleDescriptorFactory.java
index 1f271a9..afc2fe5 100644
--- a/subprojects/core-impl/src/main/groovy/org/gradle/api/internal/artifacts/ivyservice/moduleconverter/ModuleDescriptorFactory.java
+++ b/subprojects/core-impl/src/main/groovy/org/gradle/api/internal/artifacts/ivyservice/moduleconverter/ModuleDescriptorFactory.java
@@ -18,9 +18,6 @@ package org.gradle.api.internal.artifacts.ivyservice.moduleconverter;
 import org.apache.ivy.core.module.descriptor.DefaultModuleDescriptor;
 import org.gradle.api.artifacts.Module;
 
-/**
- * @author Hans Dockter
- */
 public interface ModuleDescriptorFactory {
     DefaultModuleDescriptor createModuleDescriptor(Module module);
 }
diff --git a/subprojects/core-impl/src/main/groovy/org/gradle/api/internal/artifacts/ivyservice/moduleconverter/PublishModuleDescriptorConverter.java b/subprojects/core-impl/src/main/groovy/org/gradle/api/internal/artifacts/ivyservice/moduleconverter/PublishModuleDescriptorConverter.java
index 9f1f656..f98006d 100644
--- a/subprojects/core-impl/src/main/groovy/org/gradle/api/internal/artifacts/ivyservice/moduleconverter/PublishModuleDescriptorConverter.java
+++ b/subprojects/core-impl/src/main/groovy/org/gradle/api/internal/artifacts/ivyservice/moduleconverter/PublishModuleDescriptorConverter.java
@@ -17,17 +17,14 @@
 package org.gradle.api.internal.artifacts.ivyservice.moduleconverter;
 
 import org.apache.ivy.core.module.descriptor.DefaultModuleDescriptor;
-import org.apache.ivy.core.module.descriptor.ModuleDescriptor;
 import org.gradle.api.artifacts.Configuration;
 import org.gradle.api.artifacts.Module;
-import org.gradle.api.artifacts.ModuleDependency;
+import org.gradle.api.internal.artifacts.BuildableModuleVersionPublishMetaData;
+import org.gradle.api.internal.artifacts.ModuleVersionPublishMetaData;
 import org.gradle.api.internal.artifacts.ivyservice.ModuleDescriptorConverter;
 
 import java.util.Set;
 
-/**
- * @author Hans Dockter
- */
 public class PublishModuleDescriptorConverter implements ModuleDescriptorConverter {
     static final String IVY_MAVEN_NAMESPACE = "http://ant.apache.org/ivy/maven";
     static final String IVY_MAVEN_NAMESPACE_PREFIX = "m";
@@ -41,19 +38,11 @@ public class PublishModuleDescriptorConverter implements ModuleDescriptorConvert
         this.artifactsToModuleDescriptorConverter = artifactsToModuleDescriptorConverter;
     }
 
-    public ModuleDescriptor convert(Set<? extends Configuration> configurations, Module module) {
-         DefaultModuleDescriptor moduleDescriptor = (DefaultModuleDescriptor) resolveModuleDescriptorConverter
-                .convert(configurations, module);
+    public ModuleVersionPublishMetaData convert(Set<? extends Configuration> configurations, Module module) {
+        BuildableModuleVersionPublishMetaData publishMetaData = (BuildableModuleVersionPublishMetaData) resolveModuleDescriptorConverter.convert(configurations, module);
+        DefaultModuleDescriptor moduleDescriptor = publishMetaData.getModuleDescriptor();
         moduleDescriptor.addExtraAttributeNamespace(IVY_MAVEN_NAMESPACE_PREFIX, IVY_MAVEN_NAMESPACE);
-        artifactsToModuleDescriptorConverter.addArtifacts(moduleDescriptor, configurations);
-        return moduleDescriptor;
-    }
-
-    public ModuleDescriptor createModuleDescriptor(Module module) {
-        return resolveModuleDescriptorConverter.createModuleDescriptor(module);
-    }
-
-    public void addDependencyDescriptor(String configuration, DefaultModuleDescriptor moduleDescriptor, ModuleDependency dependency) {
-        resolveModuleDescriptorConverter.addDependencyDescriptor(configuration, moduleDescriptor, dependency);
+        artifactsToModuleDescriptorConverter.addArtifacts(publishMetaData, configurations);
+        return publishMetaData;
     }
 }
diff --git a/subprojects/core-impl/src/main/groovy/org/gradle/api/internal/artifacts/ivyservice/moduleconverter/ResolveModuleDescriptorConverter.java b/subprojects/core-impl/src/main/groovy/org/gradle/api/internal/artifacts/ivyservice/moduleconverter/ResolveModuleDescriptorConverter.java
index c29ccb0..388bc6f 100644
--- a/subprojects/core-impl/src/main/groovy/org/gradle/api/internal/artifacts/ivyservice/moduleconverter/ResolveModuleDescriptorConverter.java
+++ b/subprojects/core-impl/src/main/groovy/org/gradle/api/internal/artifacts/ivyservice/moduleconverter/ResolveModuleDescriptorConverter.java
@@ -17,48 +17,33 @@
 package org.gradle.api.internal.artifacts.ivyservice.moduleconverter;
 
 import org.apache.ivy.core.module.descriptor.DefaultModuleDescriptor;
-import org.apache.ivy.core.module.descriptor.ModuleDescriptor;
 import org.gradle.api.artifacts.Configuration;
 import org.gradle.api.artifacts.Module;
-import org.gradle.api.artifacts.ModuleDependency;
+import org.gradle.api.internal.artifacts.BuildableModuleVersionPublishMetaData;
+import org.gradle.api.internal.artifacts.DefaultModuleVersionPublishMetaData;
 import org.gradle.api.internal.artifacts.ivyservice.ModuleDescriptorConverter;
 import org.gradle.api.internal.artifacts.ivyservice.moduleconverter.dependencies.DependenciesToModuleDescriptorConverter;
-import org.gradle.api.internal.artifacts.ivyservice.moduleconverter.dependencies.DependencyDescriptorFactory;
 
 import java.util.Set;
 
-/**
- * @author Hans Dockter
- */
 public class ResolveModuleDescriptorConverter implements ModuleDescriptorConverter {
     private final ModuleDescriptorFactory moduleDescriptorFactory;
-    private final DependencyDescriptorFactory dependencyDescriptorFactory;
     private final ConfigurationsToModuleDescriptorConverter configurationsToModuleDescriptorConverter;
     private final DependenciesToModuleDescriptorConverter dependenciesToModuleDescriptorConverter;
 
     public ResolveModuleDescriptorConverter(ModuleDescriptorFactory moduleDescriptorFactory,
-                                            DependencyDescriptorFactory dependencyDescriptorFactory,
                                             ConfigurationsToModuleDescriptorConverter configurationsToModuleDescriptorConverter,
                                             DependenciesToModuleDescriptorConverter dependenciesToModuleDescriptorConverter) {
         this.moduleDescriptorFactory = moduleDescriptorFactory;
-        this.dependencyDescriptorFactory = dependencyDescriptorFactory;
         this.configurationsToModuleDescriptorConverter = configurationsToModuleDescriptorConverter;
         this.dependenciesToModuleDescriptorConverter = dependenciesToModuleDescriptorConverter;
     }
 
-    public ModuleDescriptor convert(Set<? extends Configuration> configurations, Module module) {
+    public BuildableModuleVersionPublishMetaData convert(Set<? extends Configuration> configurations, Module module) {
         assert configurations.size() > 0 : "No configurations found for module: " + module.getName() + ". Configure them or apply a plugin that does it.";
         DefaultModuleDescriptor moduleDescriptor = moduleDescriptorFactory.createModuleDescriptor(module);
         configurationsToModuleDescriptorConverter.addConfigurations(moduleDescriptor, configurations);
         dependenciesToModuleDescriptorConverter.addDependencyDescriptors(moduleDescriptor, configurations);
-        return moduleDescriptor;
-    }
-
-    public ModuleDescriptor createModuleDescriptor(Module module) {
-        return moduleDescriptorFactory.createModuleDescriptor(module);
-    }
-
-    public void addDependencyDescriptor(String configuration, DefaultModuleDescriptor moduleDescriptor, ModuleDependency dependency) {
-        dependencyDescriptorFactory.addDependencyDescriptor(configuration, moduleDescriptor, dependency);
+        return new DefaultModuleVersionPublishMetaData(moduleDescriptor);
     }
 }
diff --git a/subprojects/core-impl/src/main/groovy/org/gradle/api/internal/artifacts/ivyservice/moduleconverter/dependencies/AbstractIvyDependencyDescriptorFactory.java b/subprojects/core-impl/src/main/groovy/org/gradle/api/internal/artifacts/ivyservice/moduleconverter/dependencies/AbstractIvyDependencyDescriptorFactory.java
index 4e8d20e..60d7fd6 100644
--- a/subprojects/core-impl/src/main/groovy/org/gradle/api/internal/artifacts/ivyservice/moduleconverter/dependencies/AbstractIvyDependencyDescriptorFactory.java
+++ b/subprojects/core-impl/src/main/groovy/org/gradle/api/internal/artifacts/ivyservice/moduleconverter/dependencies/AbstractIvyDependencyDescriptorFactory.java
@@ -30,9 +30,6 @@ import java.net.MalformedURLException;
 import java.net.URL;
 import java.util.Set;
 
-/**
- * @author Hans Dockter
- */
 public abstract class AbstractIvyDependencyDescriptorFactory implements IvyDependencyDescriptorFactory {
     private ExcludeRuleConverter excludeRuleConverter;
 
diff --git a/subprojects/core-impl/src/main/groovy/org/gradle/api/internal/artifacts/ivyservice/moduleconverter/dependencies/ClientModuleIvyDependencyDescriptorFactory.java b/subprojects/core-impl/src/main/groovy/org/gradle/api/internal/artifacts/ivyservice/moduleconverter/dependencies/ClientModuleIvyDependencyDescriptorFactory.java
index c4677cc..eeb8d0b 100644
--- a/subprojects/core-impl/src/main/groovy/org/gradle/api/internal/artifacts/ivyservice/moduleconverter/dependencies/ClientModuleIvyDependencyDescriptorFactory.java
+++ b/subprojects/core-impl/src/main/groovy/org/gradle/api/internal/artifacts/ivyservice/moduleconverter/dependencies/ClientModuleIvyDependencyDescriptorFactory.java
@@ -22,9 +22,6 @@ import org.gradle.api.artifacts.ModuleDependency;
 import org.gradle.api.internal.artifacts.ivyservice.IvyUtil;
 import org.gradle.api.internal.artifacts.ivyservice.moduleconverter.ExcludeRuleConverter;
 
-/**
- * @author Hans Dockter
-*/
 public class ClientModuleIvyDependencyDescriptorFactory extends AbstractIvyDependencyDescriptorFactory {
     private ModuleDescriptorFactoryForClientModule moduleDescriptorFactoryForClientModule;
 
diff --git a/subprojects/core-impl/src/main/groovy/org/gradle/api/internal/artifacts/ivyservice/moduleconverter/dependencies/DefaultDependenciesToModuleDescriptorConverter.java b/subprojects/core-impl/src/main/groovy/org/gradle/api/internal/artifacts/ivyservice/moduleconverter/dependencies/DefaultDependenciesToModuleDescriptorConverter.java
index 625accc..c50beff 100644
--- a/subprojects/core-impl/src/main/groovy/org/gradle/api/internal/artifacts/ivyservice/moduleconverter/dependencies/DefaultDependenciesToModuleDescriptorConverter.java
+++ b/subprojects/core-impl/src/main/groovy/org/gradle/api/internal/artifacts/ivyservice/moduleconverter/dependencies/DefaultDependenciesToModuleDescriptorConverter.java
@@ -23,9 +23,6 @@ import org.gradle.api.internal.artifacts.ivyservice.moduleconverter.ExcludeRuleC
 
 import java.util.Collection;
 
-/**
- * @author Hans Dockter
- */
 public class DefaultDependenciesToModuleDescriptorConverter implements DependenciesToModuleDescriptorConverter {
     private DependencyDescriptorFactory dependencyDescriptorFactory;
     private ExcludeRuleConverter excludeRuleConverter;
diff --git a/subprojects/core-impl/src/main/groovy/org/gradle/api/internal/artifacts/ivyservice/moduleconverter/dependencies/DefaultDependencyDescriptorFactory.java b/subprojects/core-impl/src/main/groovy/org/gradle/api/internal/artifacts/ivyservice/moduleconverter/dependencies/DefaultDependencyDescriptorFactory.java
index 23bba49..8ac55c4 100644
--- a/subprojects/core-impl/src/main/groovy/org/gradle/api/internal/artifacts/ivyservice/moduleconverter/dependencies/DefaultDependencyDescriptorFactory.java
+++ b/subprojects/core-impl/src/main/groovy/org/gradle/api/internal/artifacts/ivyservice/moduleconverter/dependencies/DefaultDependencyDescriptorFactory.java
@@ -23,9 +23,6 @@ import org.gradle.util.WrapUtil;
 
 import java.util.List;
 
-/**
- * @author Hans Dockter
- */
 public class DefaultDependencyDescriptorFactory implements DependencyDescriptorFactory {
     private List<IvyDependencyDescriptorFactory> dependencyDescriptorFactories;
 
diff --git a/subprojects/core-impl/src/main/groovy/org/gradle/api/internal/artifacts/ivyservice/moduleconverter/dependencies/DefaultModuleDescriptorFactoryForClientModule.java b/subprojects/core-impl/src/main/groovy/org/gradle/api/internal/artifacts/ivyservice/moduleconverter/dependencies/DefaultModuleDescriptorFactoryForClientModule.java
index f55f989..24b5aeb 100644
--- a/subprojects/core-impl/src/main/groovy/org/gradle/api/internal/artifacts/ivyservice/moduleconverter/dependencies/DefaultModuleDescriptorFactoryForClientModule.java
+++ b/subprojects/core-impl/src/main/groovy/org/gradle/api/internal/artifacts/ivyservice/moduleconverter/dependencies/DefaultModuleDescriptorFactoryForClientModule.java
@@ -25,11 +25,8 @@ import org.gradle.api.artifacts.ModuleDependency;
 
 import java.util.Set;
 
-/**
- * @author Hans Dockter
- */
 public class DefaultModuleDescriptorFactoryForClientModule implements ModuleDescriptorFactoryForClientModule {
-    // Because of bi directioal dependencies we need setter injection
+    // Because of bidirectional dependencies we need setter injection
     private DependencyDescriptorFactory dependencyDescriptorFactory;
 
     public ModuleDescriptor createModuleDescriptor(ModuleRevisionId moduleRevisionId, Set<ModuleDependency> dependencies) {
diff --git a/subprojects/core-impl/src/main/groovy/org/gradle/api/internal/artifacts/ivyservice/moduleconverter/dependencies/DependenciesToModuleDescriptorConverter.java b/subprojects/core-impl/src/main/groovy/org/gradle/api/internal/artifacts/ivyservice/moduleconverter/dependencies/DependenciesToModuleDescriptorConverter.java
index 3dc12b0..91ce740 100644
--- a/subprojects/core-impl/src/main/groovy/org/gradle/api/internal/artifacts/ivyservice/moduleconverter/dependencies/DependenciesToModuleDescriptorConverter.java
+++ b/subprojects/core-impl/src/main/groovy/org/gradle/api/internal/artifacts/ivyservice/moduleconverter/dependencies/DependenciesToModuleDescriptorConverter.java
@@ -20,9 +20,6 @@ import org.gradle.api.artifacts.Configuration;
 
 import java.util.Collection;
 
-/**
- * @author Hans Dockter
- */
 public interface DependenciesToModuleDescriptorConverter {
     void addDependencyDescriptors(DefaultModuleDescriptor moduleDescriptor, Collection<? extends Configuration> configurations);
 }
diff --git a/subprojects/core-impl/src/main/groovy/org/gradle/api/internal/artifacts/ivyservice/moduleconverter/dependencies/DependencyDescriptorFactory.java b/subprojects/core-impl/src/main/groovy/org/gradle/api/internal/artifacts/ivyservice/moduleconverter/dependencies/DependencyDescriptorFactory.java
index c5dbf33..5073472 100644
--- a/subprojects/core-impl/src/main/groovy/org/gradle/api/internal/artifacts/ivyservice/moduleconverter/dependencies/DependencyDescriptorFactory.java
+++ b/subprojects/core-impl/src/main/groovy/org/gradle/api/internal/artifacts/ivyservice/moduleconverter/dependencies/DependencyDescriptorFactory.java
@@ -18,9 +18,6 @@ package org.gradle.api.internal.artifacts.ivyservice.moduleconverter.dependencie
 import org.apache.ivy.core.module.descriptor.DefaultModuleDescriptor;
 import org.gradle.api.artifacts.ModuleDependency;
 
-/**
- * @author Hans Dockter
- */
 public interface DependencyDescriptorFactory {
     void addDependencyDescriptor(String configuration, DefaultModuleDescriptor moduleDescriptor, ModuleDependency dependency);
 }
diff --git a/subprojects/core-impl/src/main/groovy/org/gradle/api/internal/artifacts/ivyservice/moduleconverter/dependencies/ExternalModuleIvyDependencyDescriptorFactory.java b/subprojects/core-impl/src/main/groovy/org/gradle/api/internal/artifacts/ivyservice/moduleconverter/dependencies/ExternalModuleIvyDependencyDescriptorFactory.java
index f885084..0cffb8b 100644
--- a/subprojects/core-impl/src/main/groovy/org/gradle/api/internal/artifacts/ivyservice/moduleconverter/dependencies/ExternalModuleIvyDependencyDescriptorFactory.java
+++ b/subprojects/core-impl/src/main/groovy/org/gradle/api/internal/artifacts/ivyservice/moduleconverter/dependencies/ExternalModuleIvyDependencyDescriptorFactory.java
@@ -22,9 +22,6 @@ import org.gradle.api.artifacts.ModuleDependency;
 import org.gradle.api.internal.artifacts.ivyservice.IvyUtil;
 import org.gradle.api.internal.artifacts.ivyservice.moduleconverter.ExcludeRuleConverter;
 
-/**
- * @author Hans Dockter
-*/
 public class ExternalModuleIvyDependencyDescriptorFactory extends AbstractIvyDependencyDescriptorFactory {
     public ExternalModuleIvyDependencyDescriptorFactory(ExcludeRuleConverter excludeRuleConverter) {
         super(excludeRuleConverter);
diff --git a/subprojects/core-impl/src/main/groovy/org/gradle/api/internal/artifacts/ivyservice/moduleconverter/dependencies/ModuleDescriptorFactoryForClientModule.java b/subprojects/core-impl/src/main/groovy/org/gradle/api/internal/artifacts/ivyservice/moduleconverter/dependencies/ModuleDescriptorFactoryForClientModule.java
index 760cf15..0770a7e 100644
--- a/subprojects/core-impl/src/main/groovy/org/gradle/api/internal/artifacts/ivyservice/moduleconverter/dependencies/ModuleDescriptorFactoryForClientModule.java
+++ b/subprojects/core-impl/src/main/groovy/org/gradle/api/internal/artifacts/ivyservice/moduleconverter/dependencies/ModuleDescriptorFactoryForClientModule.java
@@ -21,9 +21,6 @@ import org.gradle.api.artifacts.ModuleDependency;
 
 import java.util.Set;
 
-/**
- * @author Hans Dockter
- */
 public interface ModuleDescriptorFactoryForClientModule {
     ModuleDescriptor createModuleDescriptor(ModuleRevisionId moduleRevisionId, Set<ModuleDependency> dependencies);
 }
diff --git a/subprojects/core-impl/src/main/groovy/org/gradle/api/internal/artifacts/ivyservice/moduleconverter/dependencies/ProjectIvyDependencyDescriptorFactory.java b/subprojects/core-impl/src/main/groovy/org/gradle/api/internal/artifacts/ivyservice/moduleconverter/dependencies/ProjectIvyDependencyDescriptorFactory.java
index 33e755b..fb16663 100644
--- a/subprojects/core-impl/src/main/groovy/org/gradle/api/internal/artifacts/ivyservice/moduleconverter/dependencies/ProjectIvyDependencyDescriptorFactory.java
+++ b/subprojects/core-impl/src/main/groovy/org/gradle/api/internal/artifacts/ivyservice/moduleconverter/dependencies/ProjectIvyDependencyDescriptorFactory.java
@@ -1,5 +1,5 @@
 /*
- * Copyright 2009 the original author or authors.
+ * Copyright 2013 the original author or authors.
  *
  * Licensed under the Apache License, Version 2.0 (the "License");
  * you may not use this file except in compliance with the License.
@@ -20,21 +20,20 @@ import org.apache.ivy.core.module.id.ModuleRevisionId;
 import org.gradle.api.artifacts.Module;
 import org.gradle.api.artifacts.ModuleDependency;
 import org.gradle.api.artifacts.ProjectDependency;
+import org.gradle.api.internal.artifacts.dependencies.ProjectDependencyInternal;
 import org.gradle.api.internal.artifacts.ivyservice.IvyUtil;
 import org.gradle.api.internal.artifacts.ivyservice.moduleconverter.ExcludeRuleConverter;
 import org.gradle.api.internal.project.ProjectInternal;
 
-/**
- * @author Hans Dockter
- */
 public class ProjectIvyDependencyDescriptorFactory extends AbstractIvyDependencyDescriptorFactory {
     public ProjectIvyDependencyDescriptorFactory(ExcludeRuleConverter excludeRuleConverter) {
         super(excludeRuleConverter);
     }
 
     public EnhancedDependencyDescriptor createDependencyDescriptor(String configuration, ModuleDependency dependency, ModuleDescriptor parent) {
+        ProjectDependencyInternal projectDependency = (ProjectDependencyInternal) dependency;
+        projectDependency.beforeResolved();
         ModuleRevisionId moduleRevisionId = createModuleRevisionId(dependency);
-        ProjectDependency projectDependency = (ProjectDependency) dependency;
         ProjectDependencyDescriptor dependencyDescriptor = new ProjectDependencyDescriptor(projectDependency, parent, moduleRevisionId, false, false, dependency.isTransitive());
         addExcludesArtifactsAndDependencies(configuration, dependency, dependencyDescriptor);
         return dependencyDescriptor;
diff --git a/subprojects/core-impl/src/main/groovy/org/gradle/api/internal/artifacts/ivyservice/projectmodule/DefaultProjectModuleRegistry.java b/subprojects/core-impl/src/main/groovy/org/gradle/api/internal/artifacts/ivyservice/projectmodule/DefaultProjectModuleRegistry.java
index 0b6b02c..c98b93a 100644
--- a/subprojects/core-impl/src/main/groovy/org/gradle/api/internal/artifacts/ivyservice/projectmodule/DefaultProjectModuleRegistry.java
+++ b/subprojects/core-impl/src/main/groovy/org/gradle/api/internal/artifacts/ivyservice/projectmodule/DefaultProjectModuleRegistry.java
@@ -15,15 +15,11 @@
  */
 package org.gradle.api.internal.artifacts.ivyservice.projectmodule;
 
-import org.apache.ivy.core.module.descriptor.Artifact;
-import org.apache.ivy.core.module.descriptor.DependencyArtifactDescriptor;
-import org.apache.ivy.core.module.descriptor.ModuleDescriptor;
 import org.gradle.api.artifacts.Module;
-import org.gradle.api.internal.artifacts.ivyservice.DefaultIvyDependencyPublisher;
+import org.gradle.api.internal.artifacts.ModuleVersionPublishMetaData;
 import org.gradle.api.internal.artifacts.ivyservice.ModuleDescriptorConverter;
 import org.gradle.api.internal.artifacts.ivyservice.moduleconverter.dependencies.ProjectDependencyDescriptor;
 import org.gradle.api.internal.project.ProjectInternal;
-import org.gradle.util.ReflectionUtil;
 
 public class DefaultProjectModuleRegistry implements ProjectModuleRegistry {
     private final ModuleDescriptorConverter moduleDescriptorConverter;
@@ -32,22 +28,9 @@ public class DefaultProjectModuleRegistry implements ProjectModuleRegistry {
         this.moduleDescriptorConverter = moduleDescriptorConverter;
     }
 
-    public ModuleDescriptor findProject(ProjectDependencyDescriptor descriptor) {
+    public ModuleVersionPublishMetaData findProject(ProjectDependencyDescriptor descriptor) {
         ProjectInternal project = descriptor.getTargetProject();
         Module projectModule = project.getModule();
-        ModuleDescriptor projectDescriptor = moduleDescriptorConverter.convert(project.getConfigurations(), projectModule);
-
-        for (DependencyArtifactDescriptor artifactDescriptor : descriptor.getAllDependencyArtifacts()) {
-            for (Artifact artifact : projectDescriptor.getAllArtifacts()) {
-                if (artifact.getName().equals(artifactDescriptor.getName()) && artifact.getExt().equals(
-                        artifactDescriptor.getExt())) {
-                    String path = artifact.getExtraAttribute(DefaultIvyDependencyPublisher.FILE_ABSOLUTE_PATH_EXTRA_ATTRIBUTE);
-                    ReflectionUtil.invoke(artifactDescriptor, "setExtraAttribute",
-                            new Object[]{DefaultIvyDependencyPublisher.FILE_ABSOLUTE_PATH_EXTRA_ATTRIBUTE, path});
-                }
-            }
-        }
-
-        return projectDescriptor;
+        return moduleDescriptorConverter.convert(project.getConfigurations(), projectModule);
     }
 }
diff --git a/subprojects/core-impl/src/main/groovy/org/gradle/api/internal/artifacts/ivyservice/projectmodule/ProjectDependencyResolver.java b/subprojects/core-impl/src/main/groovy/org/gradle/api/internal/artifacts/ivyservice/projectmodule/ProjectDependencyResolver.java
index 54d67c2..d209d92 100644
--- a/subprojects/core-impl/src/main/groovy/org/gradle/api/internal/artifacts/ivyservice/projectmodule/ProjectDependencyResolver.java
+++ b/subprojects/core-impl/src/main/groovy/org/gradle/api/internal/artifacts/ivyservice/projectmodule/ProjectDependencyResolver.java
@@ -1,5 +1,5 @@
 /*
- * Copyright 2009 the original author or authors.
+ * Copyright 2013 the original author 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,46 +18,65 @@ package org.gradle.api.internal.artifacts.ivyservice.projectmodule;
 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.gradle.api.internal.artifacts.DefaultModuleVersionIdentifier;
+import org.gradle.api.artifacts.Configuration;
+import org.gradle.api.artifacts.Module;
+import org.gradle.api.artifacts.ModuleVersionIdentifier;
+import org.gradle.api.internal.artifacts.DefaultArtifactIdentifier;
+import org.gradle.api.internal.artifacts.ModuleVersionPublishMetaData;
 import org.gradle.api.internal.artifacts.ivyservice.*;
 import org.gradle.api.internal.artifacts.ivyservice.ivyresolve.DependencyMetaData;
 import org.gradle.api.internal.artifacts.ivyservice.moduleconverter.dependencies.ProjectDependencyDescriptor;
-import org.gradle.initialization.ProjectAccessListener;
 
 import java.io.File;
+import java.util.Map;
+import java.util.Set;
 
-public class ProjectDependencyResolver implements DependencyToModuleResolver {
+public class ProjectDependencyResolver implements DependencyToModuleVersionResolver, ModuleToModuleVersionResolver {
     private final ProjectModuleRegistry projectModuleRegistry;
-    private final DependencyToModuleResolver resolver;
-    private final ProjectArtifactResolver artifactResolver;
-    private final ProjectAccessListener projectAccessListener;
+    private final DependencyToModuleVersionResolver resolver;
+    private final ModuleDescriptorConverter moduleDescriptorConverter;
 
-    public ProjectDependencyResolver(ProjectModuleRegistry projectModuleRegistry, DependencyToModuleResolver resolver, ProjectAccessListener projectAccessListener) {
+    public ProjectDependencyResolver(ProjectModuleRegistry projectModuleRegistry, DependencyToModuleVersionResolver resolver, ModuleDescriptorConverter moduleDescriptorConverter) {
         this.projectModuleRegistry = projectModuleRegistry;
         this.resolver = resolver;
-        this.projectAccessListener = projectAccessListener;
-        artifactResolver = new ProjectArtifactResolver();
+        this.moduleDescriptorConverter = moduleDescriptorConverter;
     }
 
     public void resolve(DependencyMetaData dependency, BuildableModuleVersionResolveResult result) {
         DependencyDescriptor descriptor = dependency.getDescriptor();
         if (descriptor instanceof ProjectDependencyDescriptor) {
             ProjectDependencyDescriptor desc = (ProjectDependencyDescriptor) descriptor;
-            projectAccessListener.beforeResolvingProjectDependency(desc.getTargetProject());
-            ModuleDescriptor moduleDescriptor = projectModuleRegistry.findProject(desc);
-            final ModuleRevisionId moduleRevisionId = moduleDescriptor.getModuleRevisionId();
-            final DefaultModuleVersionIdentifier moduleVersionIdentifier = new DefaultModuleVersionIdentifier(moduleRevisionId.getOrganisation(), moduleRevisionId.getName(), moduleRevisionId.getRevision());
-            result.resolved(moduleVersionIdentifier, moduleDescriptor, artifactResolver);
+            ModuleVersionPublishMetaData publishMetaData = projectModuleRegistry.findProject(desc);
+            ModuleDescriptor moduleDescriptor = publishMetaData.getModuleDescriptor();
+            ModuleVersionIdentifier moduleVersionIdentifier = publishMetaData.getId();
+            result.resolved(moduleVersionIdentifier, moduleDescriptor, new ProjectArtifactResolver(publishMetaData));
         } else {
             resolver.resolve(dependency, result);
         }
     }
 
+    public void resolve(Module module, Set<? extends Configuration> configurations, BuildableModuleVersionResolveResult result) {
+        ModuleVersionPublishMetaData publishMetaData = moduleDescriptorConverter.convert(configurations, module);
+        ModuleDescriptor moduleDescriptor = publishMetaData.getModuleDescriptor();
+        ModuleVersionIdentifier moduleVersionIdentifier = publishMetaData.getId();
+        result.resolved(moduleVersionIdentifier, moduleDescriptor, new ProjectArtifactResolver(publishMetaData));
+    }
+
     private static class ProjectArtifactResolver implements ArtifactResolver {
+        private final ModuleVersionPublishMetaData publishMetaData;
+
+        public ProjectArtifactResolver(ModuleVersionPublishMetaData publishMetaData) {
+            this.publishMetaData = publishMetaData;
+        }
+
         public void resolve(Artifact artifact, BuildableArtifactResolveResult result) {
-            String path = artifact.getExtraAttribute(DefaultIvyDependencyPublisher.FILE_ABSOLUTE_PATH_EXTRA_ATTRIBUTE);
-            result.resolved(new File(path));
+            for (Map.Entry<Artifact, File> entry : publishMetaData.getArtifacts().entrySet()) {
+                if (entry.getKey().getId().equals(artifact.getId())) {
+                    result.resolved(entry.getValue());
+                    return;
+                }
+            }
+            result.notFound(new DefaultArtifactIdentifier(artifact));
         }
     }
 }
diff --git a/subprojects/core-impl/src/main/groovy/org/gradle/api/internal/artifacts/ivyservice/projectmodule/ProjectModuleRegistry.java b/subprojects/core-impl/src/main/groovy/org/gradle/api/internal/artifacts/ivyservice/projectmodule/ProjectModuleRegistry.java
index 6ffc502..f85a1c7 100644
--- a/subprojects/core-impl/src/main/groovy/org/gradle/api/internal/artifacts/ivyservice/projectmodule/ProjectModuleRegistry.java
+++ b/subprojects/core-impl/src/main/groovy/org/gradle/api/internal/artifacts/ivyservice/projectmodule/ProjectModuleRegistry.java
@@ -15,12 +15,12 @@
  */
 package org.gradle.api.internal.artifacts.ivyservice.projectmodule;
 
-import org.apache.ivy.core.module.descriptor.ModuleDescriptor;
+import org.gradle.api.internal.artifacts.ModuleVersionPublishMetaData;
 import org.gradle.api.internal.artifacts.ivyservice.moduleconverter.dependencies.ProjectDependencyDescriptor;
 
 /**
  * TODO - this probably should lookup a project by id, much like ClientModuleRegistry. Then, there would be no dependency on ivy DependencyDescriptor.
  */
 public interface ProjectModuleRegistry {
-    ModuleDescriptor findProject(ProjectDependencyDescriptor descriptor);
+    ModuleVersionPublishMetaData findProject(ProjectDependencyDescriptor descriptor);
 }
diff --git a/subprojects/core-impl/src/main/groovy/org/gradle/api/internal/artifacts/ivyservice/resolutionstrategy/DefaultResolutionStrategy.java b/subprojects/core-impl/src/main/groovy/org/gradle/api/internal/artifacts/ivyservice/resolutionstrategy/DefaultResolutionStrategy.java
index 740b51a..c9f5ef7 100644
--- a/subprojects/core-impl/src/main/groovy/org/gradle/api/internal/artifacts/ivyservice/resolutionstrategy/DefaultResolutionStrategy.java
+++ b/subprojects/core-impl/src/main/groovy/org/gradle/api/internal/artifacts/ivyservice/resolutionstrategy/DefaultResolutionStrategy.java
@@ -36,9 +36,6 @@ import java.util.concurrent.TimeUnit;
 
 import static org.gradle.util.GUtil.flattenElements;
 
-/**
- * by Szczepan Faber, created at: 10/7/11
- */
 public class DefaultResolutionStrategy implements ResolutionStrategyInternal {
 
     private Set<ModuleVersionSelector> forcedModules = new LinkedHashSet<ModuleVersionSelector>();
diff --git a/subprojects/core-impl/src/main/groovy/org/gradle/api/internal/artifacts/ivyservice/resolutionstrategy/LatestConflictResolution.java b/subprojects/core-impl/src/main/groovy/org/gradle/api/internal/artifacts/ivyservice/resolutionstrategy/LatestConflictResolution.java
index a8022db..860b490 100644
--- a/subprojects/core-impl/src/main/groovy/org/gradle/api/internal/artifacts/ivyservice/resolutionstrategy/LatestConflictResolution.java
+++ b/subprojects/core-impl/src/main/groovy/org/gradle/api/internal/artifacts/ivyservice/resolutionstrategy/LatestConflictResolution.java
@@ -20,8 +20,6 @@ import org.gradle.api.artifacts.ConflictResolution;
 
 /**
  * Latest resolution strategy
- * <p>
- * by Szczepan Faber, created at: 10/5/11
  */
 public class LatestConflictResolution implements ConflictResolution {
 }
diff --git a/subprojects/core-impl/src/main/groovy/org/gradle/api/internal/artifacts/ivyservice/resolutionstrategy/ModuleForcingResolveRule.java b/subprojects/core-impl/src/main/groovy/org/gradle/api/internal/artifacts/ivyservice/resolutionstrategy/ModuleForcingResolveRule.java
index 4992512..a227620 100644
--- a/subprojects/core-impl/src/main/groovy/org/gradle/api/internal/artifacts/ivyservice/resolutionstrategy/ModuleForcingResolveRule.java
+++ b/subprojects/core-impl/src/main/groovy/org/gradle/api/internal/artifacts/ivyservice/resolutionstrategy/ModuleForcingResolveRule.java
@@ -27,9 +27,6 @@ import java.util.Collection;
 import java.util.HashMap;
 import java.util.Map;
 
-/**
-* by Szczepan Faber, created at: 11/29/12
-*/
 public class ModuleForcingResolveRule implements Action<DependencyResolveDetailsInternal> {
 
     private final Map<ModuleIdentifier, String> forcedModules;
diff --git a/subprojects/core-impl/src/main/groovy/org/gradle/api/internal/artifacts/ivyservice/resolutionstrategy/StrictConflictResolution.java b/subprojects/core-impl/src/main/groovy/org/gradle/api/internal/artifacts/ivyservice/resolutionstrategy/StrictConflictResolution.java
index 4ee1533..3ec3377 100644
--- a/subprojects/core-impl/src/main/groovy/org/gradle/api/internal/artifacts/ivyservice/resolutionstrategy/StrictConflictResolution.java
+++ b/subprojects/core-impl/src/main/groovy/org/gradle/api/internal/artifacts/ivyservice/resolutionstrategy/StrictConflictResolution.java
@@ -20,8 +20,6 @@ import org.gradle.api.artifacts.ConflictResolution;
 
 /**
  * Strict type, allows configuring (forcing) certain dependency versions using dependency notation
- * <p>
- * by Szczepan Faber, created at: 10/5/11
  */
 public class StrictConflictResolution implements ConflictResolution {
 }
\ No newline at end of file
diff --git a/subprojects/core-impl/src/main/groovy/org/gradle/api/internal/artifacts/ivyservice/resolveengine/DefaultDependencyResolver.java b/subprojects/core-impl/src/main/groovy/org/gradle/api/internal/artifacts/ivyservice/resolveengine/DefaultDependencyResolver.java
index e572e6b..852b2b9 100755
--- a/subprojects/core-impl/src/main/groovy/org/gradle/api/internal/artifacts/ivyservice/resolveengine/DefaultDependencyResolver.java
+++ b/subprojects/core-impl/src/main/groovy/org/gradle/api/internal/artifacts/ivyservice/resolveengine/DefaultDependencyResolver.java
@@ -15,7 +15,10 @@
  */
 package org.gradle.api.internal.artifacts.ivyservice.resolveengine;
 
+import org.apache.ivy.Ivy;
+import org.gradle.api.Transformer;
 import org.gradle.api.artifacts.ResolveException;
+import org.gradle.api.artifacts.result.ResolvedModuleVersionResult;
 import org.gradle.api.internal.artifacts.ArtifactDependencyResolver;
 import org.gradle.api.internal.artifacts.ResolverResults;
 import org.gradle.api.internal.artifacts.configurations.ConfigurationInternal;
@@ -27,9 +30,15 @@ import org.gradle.api.internal.artifacts.ivyservice.ivyresolve.ResolveIvyFactory
 import org.gradle.api.internal.artifacts.ivyservice.projectmodule.ProjectDependencyResolver;
 import org.gradle.api.internal.artifacts.ivyservice.projectmodule.ProjectModuleRegistry;
 import org.gradle.api.internal.artifacts.ivyservice.resolutionstrategy.StrictConflictResolution;
-import org.gradle.api.internal.artifacts.ivyservice.resolveengine.result.ResolutionResultBuilder;
+import org.gradle.api.internal.artifacts.ivyservice.resolveengine.store.ResolutionResultsStoreFactory;
+import org.gradle.api.internal.artifacts.ivyservice.resolveengine.oldresult.DefaultResolvedConfigurationBuilder;
+import org.gradle.api.internal.artifacts.ivyservice.resolveengine.oldresult.TransientConfigurationResults;
+import org.gradle.api.internal.artifacts.ivyservice.resolveengine.oldresult.TransientResultsStore;
+import org.gradle.api.internal.artifacts.ivyservice.resolveengine.result.ResolvedConfigurationListener;
+import org.gradle.api.internal.artifacts.ivyservice.resolveengine.result.StreamingResolutionResultBuilder;
 import org.gradle.api.internal.artifacts.repositories.ResolutionAwareRepository;
-import org.gradle.initialization.ProjectAccessListener;
+import org.gradle.api.internal.cache.BinaryStore;
+import org.gradle.api.internal.cache.Store;
 import org.slf4j.Logger;
 import org.slf4j.LoggerFactory;
 
@@ -41,41 +50,58 @@ public class DefaultDependencyResolver implements ArtifactDependencyResolver {
     private final ResolvedArtifactFactory resolvedArtifactFactory;
     private final ResolveIvyFactory ivyFactory;
     private final ProjectModuleRegistry projectModuleRegistry;
-    private final ProjectAccessListener projectAccessListener;
     private final CacheLockingManager cacheLockingManager;
+    private final IvyContextManager ivyContextManager;
+    private final ResolutionResultsStoreFactory storeFactory;
 
     public DefaultDependencyResolver(ResolveIvyFactory ivyFactory, ModuleDescriptorConverter moduleDescriptorConverter, ResolvedArtifactFactory resolvedArtifactFactory,
-                                     ProjectModuleRegistry projectModuleRegistry, ProjectAccessListener projectAccessListener, CacheLockingManager cacheLockingManager) {
+                                     ProjectModuleRegistry projectModuleRegistry, CacheLockingManager cacheLockingManager, IvyContextManager ivyContextManager,
+                                     ResolutionResultsStoreFactory storeFactory) {
         this.ivyFactory = ivyFactory;
         this.moduleDescriptorConverter = moduleDescriptorConverter;
         this.resolvedArtifactFactory = resolvedArtifactFactory;
         this.projectModuleRegistry = projectModuleRegistry;
-        this.projectAccessListener = projectAccessListener;
         this.cacheLockingManager = cacheLockingManager;
+        this.ivyContextManager = ivyContextManager;
+        this.storeFactory = storeFactory;
     }
 
-    public ResolverResults resolve(ConfigurationInternal configuration, List<? extends ResolutionAwareRepository> repositories) throws ResolveException {
+    public ResolverResults resolve(final ConfigurationInternal configuration, final List<? extends ResolutionAwareRepository> repositories) throws ResolveException {
         LOGGER.debug("Resolving {}", configuration);
+        return ivyContextManager.withIvy(new Transformer<ResolverResults, Ivy>() {
+            public ResolverResults transform(Ivy ivy) {
+                IvyAdapter ivyAdapter = ivyFactory.create(configuration, repositories);
 
-        IvyAdapter ivyAdapter = ivyFactory.create(configuration, repositories);
+                DependencyToModuleVersionResolver dependencyResolver = ivyAdapter.getDependencyToModuleResolver();
+                dependencyResolver = new ClientModuleResolver(dependencyResolver);
+                ProjectDependencyResolver projectDependencyResolver = new ProjectDependencyResolver(projectModuleRegistry, dependencyResolver, moduleDescriptorConverter);
+                dependencyResolver = projectDependencyResolver;
+                DependencyToModuleVersionIdResolver idResolver = new LazyDependencyToModuleResolver(dependencyResolver, ivyAdapter.getVersionMatcher());
+                idResolver = new VersionForcingDependencyToModuleResolver(idResolver, configuration.getResolutionStrategy().getDependencyResolveRule());
 
-        DependencyToModuleResolver dependencyResolver = ivyAdapter.getDependencyToModuleResolver();
-        dependencyResolver = new ClientModuleResolver(dependencyResolver);
-        dependencyResolver = new ProjectDependencyResolver(projectModuleRegistry, dependencyResolver, projectAccessListener);
-        DependencyToModuleVersionIdResolver idResolver = new LazyDependencyToModuleResolver(dependencyResolver, ivyAdapter.getResolveData().getSettings().getVersionMatcher());
-        idResolver = new VersionForcingDependencyToModuleResolver(idResolver, configuration.getResolutionStrategy().getDependencyResolveRule());
+                ModuleConflictResolver conflictResolver;
+                if (configuration.getResolutionStrategy().getConflictResolution() instanceof StrictConflictResolution) {
+                    conflictResolver = new StrictConflictResolver();
+                } else {
+                    conflictResolver = new LatestModuleConflictResolver(ivyAdapter.getLatestStrategy());
+                }
+                conflictResolver = new VersionSelectionReasonResolver(conflictResolver);
 
-        ModuleConflictResolver conflictResolver;
-        if (configuration.getResolutionStrategy().getConflictResolution() instanceof StrictConflictResolution) {
-            conflictResolver = new StrictConflictResolver();
-        } else {
-            conflictResolver = new LatestModuleConflictResolver();
-        }
-        ModuleConflictResolver actualResolver = new VersionSelectionReasonResolver(conflictResolver);
+                DependencyGraphBuilder builder = new DependencyGraphBuilder(idResolver, projectDependencyResolver, conflictResolver, new DefaultDependencyToConfigurationResolver());
 
-        DependencyGraphBuilder builder = new DependencyGraphBuilder(moduleDescriptorConverter, resolvedArtifactFactory, idResolver, actualResolver);
-        ResolutionResultBuilder resultBuilder = new ResolutionResultBuilder();
-        DefaultLenientConfiguration result = builder.resolve(configuration, ivyAdapter.getResolveData(), resultBuilder);
-        return new ResolverResults(new DefaultResolvedConfiguration(result, cacheLockingManager), resultBuilder.getResult());
+                BinaryStore newModelStore = storeFactory.createBinaryStore("new-model");
+                Store<ResolvedModuleVersionResult> newModelCache = storeFactory.createNewModelCache(configuration);
+                ResolvedConfigurationListener newModelBuilder = new StreamingResolutionResultBuilder(newModelStore, newModelCache);
+
+                BinaryStore oldModelStore = storeFactory.createBinaryStore("old-model");
+                Store<TransientConfigurationResults> oldModelCache = storeFactory.createOldModelCache(configuration);
+                TransientResultsStore oldModelResults = new TransientResultsStore(oldModelStore, oldModelCache);
+                DefaultResolvedConfigurationBuilder oldModelBuilder = new DefaultResolvedConfigurationBuilder(resolvedArtifactFactory, oldModelResults);
+
+                builder.resolve(configuration, newModelBuilder, oldModelBuilder);
+                DefaultLenientConfiguration result = new DefaultLenientConfiguration(configuration, oldModelBuilder, cacheLockingManager);
+                return new ResolverResults(new DefaultResolvedConfiguration(result), newModelBuilder.complete());
+            }
+        });
     }
 }
diff --git a/subprojects/core-impl/src/main/groovy/org/gradle/api/internal/artifacts/ivyservice/resolveengine/DefaultDependencyToConfigurationResolver.java b/subprojects/core-impl/src/main/groovy/org/gradle/api/internal/artifacts/ivyservice/resolveengine/DefaultDependencyToConfigurationResolver.java
new file mode 100644
index 0000000..9ab07c3
--- /dev/null
+++ b/subprojects/core-impl/src/main/groovy/org/gradle/api/internal/artifacts/ivyservice/resolveengine/DefaultDependencyToConfigurationResolver.java
@@ -0,0 +1,78 @@
+/*
+ * Copyright 2013 the original author or authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY 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.resolveengine;
+
+import org.apache.ivy.core.module.descriptor.DependencyDescriptor;
+import org.apache.ivy.core.module.descriptor.ModuleDescriptor;
+import org.gradle.api.internal.artifacts.ivyservice.ivyresolve.ConfigurationMetaData;
+import org.gradle.api.internal.artifacts.ivyservice.ivyresolve.DependencyMetaData;
+import org.gradle.api.internal.artifacts.ivyservice.ivyresolve.ModuleVersionMetaData;
+
+import java.util.*;
+
+public class DefaultDependencyToConfigurationResolver implements DependencyToConfigurationResolver {
+    // TODO - don't pass in 'from' configuration - the dependency should have whatever context it needs
+    public Set<ConfigurationMetaData> resolveTargetConfigurations(DependencyMetaData dependencyMetaData, ConfigurationMetaData fromConfiguration, ModuleVersionMetaData targetModuleVersion) {
+        // TODO - resolve directly to config meta data
+        ModuleDescriptor targetDescriptor = targetModuleVersion.getDescriptor();
+        DependencyDescriptor dependencyDescriptor = dependencyMetaData.getDescriptor();
+        Set<String> targetConfigurationNames = new LinkedHashSet<String>();
+        for (String config : dependencyDescriptor.getModuleConfigurations()) {
+            if (config.equals("*") || config.equals("%")) {
+                collectTargetConfiguration(dependencyDescriptor, fromConfiguration, fromConfiguration.getName(), targetDescriptor, targetConfigurationNames);
+            } else if (fromConfiguration.getHierarchy().contains(config)) {
+                collectTargetConfiguration(dependencyDescriptor, fromConfiguration, config, targetDescriptor, targetConfigurationNames);
+            }
+        }
+
+        Set<ConfigurationMetaData> targets = new LinkedHashSet<ConfigurationMetaData>();
+        for (String targetConfigurationName : targetConfigurationNames) {
+            // TODO - move this down below
+            if (targetDescriptor.getConfiguration(targetConfigurationName) == null) {
+                throw new RuntimeException(String.format("Module version %s, configuration '%s' declares a dependency on configuration '%s' which is not declared in the module descriptor for %s",
+                        fromConfiguration.getModuleVersion().getId(), fromConfiguration.getName(),
+                        targetConfigurationName, targetModuleVersion.getId()));
+            }
+            ConfigurationMetaData targetConfiguration = targetModuleVersion.getConfiguration(targetConfigurationName);
+            targets.add(targetConfiguration);
+        }
+        return targets;
+    }
+
+    private void collectTargetConfiguration(DependencyDescriptor dependencyDescriptor, ConfigurationMetaData fromConfiguration, String mappingRhs, ModuleDescriptor targetModule, Collection<String> targetConfigs) {
+        String[] dependencyConfigurations = dependencyDescriptor.getDependencyConfigurations(mappingRhs, fromConfiguration.getName());
+        for (String target : dependencyConfigurations) {
+            String candidate = target;
+            int startFallback = candidate.indexOf('(');
+            if (startFallback >= 0) {
+                if (candidate.charAt(candidate.length() - 1) == ')') {
+                    String preferred = candidate.substring(0, startFallback);
+                    if (targetModule.getConfiguration(preferred) != null) {
+                        targetConfigs.add(preferred);
+                        continue;
+                    }
+                    candidate = candidate.substring(startFallback + 1, candidate.length() - 1);
+                }
+            }
+            if (candidate.equals("*")) {
+                Collections.addAll(targetConfigs, targetModule.getPublicConfigurationsNames());
+                continue;
+            }
+            targetConfigs.add(candidate);
+        }
+    }
+}
diff --git a/subprojects/core-impl/src/main/groovy/org/gradle/api/internal/artifacts/ivyservice/resolveengine/DependencyGraphBuilder.java b/subprojects/core-impl/src/main/groovy/org/gradle/api/internal/artifacts/ivyservice/resolveengine/DependencyGraphBuilder.java
index f01fe53..b951f61 100644
--- a/subprojects/core-impl/src/main/groovy/org/gradle/api/internal/artifacts/ivyservice/resolveengine/DependencyGraphBuilder.java
+++ b/subprojects/core-impl/src/main/groovy/org/gradle/api/internal/artifacts/ivyservice/resolveengine/DependencyGraphBuilder.java
@@ -15,26 +15,23 @@
  */
 package org.gradle.api.internal.artifacts.ivyservice.resolveengine;
 
-import org.apache.ivy.core.module.descriptor.*;
+import org.apache.ivy.core.module.descriptor.Artifact;
+import org.apache.ivy.core.module.descriptor.DefaultArtifact;
+import org.apache.ivy.core.module.descriptor.DependencyArtifactDescriptor;
+import org.apache.ivy.core.module.descriptor.DependencyDescriptor;
 import org.apache.ivy.core.module.id.ModuleId;
 import org.apache.ivy.core.module.id.ModuleRevisionId;
-import org.apache.ivy.core.resolve.IvyNode;
-import org.apache.ivy.core.resolve.ResolveData;
-import org.gradle.api.artifacts.ModuleVersionIdentifier;
-import org.gradle.api.artifacts.ModuleVersionSelector;
-import org.gradle.api.artifacts.ResolveException;
-import org.gradle.api.artifacts.ResolvedArtifact;
+import org.gradle.api.artifacts.*;
 import org.gradle.api.artifacts.result.ModuleVersionSelectionReason;
-import org.gradle.api.internal.artifacts.DefaultModuleVersionIdentifier;
-import org.gradle.api.internal.artifacts.DefaultResolvedDependency;
+import org.gradle.api.internal.artifacts.DefaultModuleIdentifier;
 import org.gradle.api.internal.artifacts.ResolvedConfigurationIdentifier;
 import org.gradle.api.internal.artifacts.configurations.ConfigurationInternal;
 import org.gradle.api.internal.artifacts.ivyservice.*;
-import org.gradle.api.internal.artifacts.ivyservice.ivyresolve.BuildableModuleVersionMetaData;
-import org.gradle.api.internal.artifacts.ivyservice.ivyresolve.DefaultBuildableModuleVersionMetaData;
+import org.gradle.api.internal.artifacts.ivyservice.ivyresolve.ConfigurationMetaData;
 import org.gradle.api.internal.artifacts.ivyservice.ivyresolve.DependencyMetaData;
 import org.gradle.api.internal.artifacts.ivyservice.ivyresolve.ModuleVersionMetaData;
 import org.gradle.api.internal.artifacts.ivyservice.moduleconverter.dependencies.EnhancedDependencyDescriptor;
+import org.gradle.api.internal.artifacts.ivyservice.resolveengine.oldresult.ResolvedConfigurationBuilder;
 import org.gradle.api.internal.artifacts.ivyservice.resolveengine.result.InternalDependencyResult;
 import org.gradle.api.internal.artifacts.ivyservice.resolveengine.result.ModuleVersionSelection;
 import org.gradle.api.internal.artifacts.ivyservice.resolveengine.result.ResolvedConfigurationListener;
@@ -44,41 +41,40 @@ import org.slf4j.LoggerFactory;
 
 import java.util.*;
 
-import static org.gradle.api.internal.artifacts.DefaultModuleVersionIdentifier.newId;
-
 public class DependencyGraphBuilder {
     private static final Logger LOGGER = LoggerFactory.getLogger(DependencyGraphBuilder.class);
-    private final ModuleDescriptorConverter moduleDescriptorConverter;
-    private final ResolvedArtifactFactory resolvedArtifactFactory;
     private final DependencyToModuleVersionIdResolver dependencyResolver;
+    private final DependencyToConfigurationResolver dependencyToConfigurationResolver;
     private final InternalConflictResolver conflictResolver;
+    private final ModuleToModuleVersionResolver moduleResolver;
 
-    public DependencyGraphBuilder(ModuleDescriptorConverter moduleDescriptorConverter, ResolvedArtifactFactory resolvedArtifactFactory, DependencyToModuleVersionIdResolver dependencyResolver, ModuleConflictResolver conflictResolver) {
-        this.moduleDescriptorConverter = moduleDescriptorConverter;
-        this.resolvedArtifactFactory = resolvedArtifactFactory;
+    public DependencyGraphBuilder(DependencyToModuleVersionIdResolver dependencyResolver,
+                                  ModuleToModuleVersionResolver moduleResolver,
+                                  ModuleConflictResolver conflictResolver,
+                                  DependencyToConfigurationResolver dependencyToConfigurationResolver) {
         this.dependencyResolver = dependencyResolver;
+        this.moduleResolver = moduleResolver;
+        this.dependencyToConfigurationResolver = dependencyToConfigurationResolver;
         this.conflictResolver = new InternalConflictResolver(conflictResolver);
     }
 
-    public DefaultLenientConfiguration resolve(ConfigurationInternal configuration, ResolveData resolveData, ResolvedConfigurationListener listener) throws ResolveException {
-        ModuleDescriptor rootModuleDescriptor = moduleDescriptorConverter.convert(configuration.getAll(), configuration.getModule());
-        BuildableModuleVersionMetaData rootMetaData = new DefaultBuildableModuleVersionMetaData();
-        rootMetaData.resolved(rootModuleDescriptor, false, null);
+    public void resolve(ConfigurationInternal configuration,
+                        ResolvedConfigurationListener listener,
+                        ResolvedConfigurationBuilder configurationBuilder) throws ResolveException {
+        DefaultBuildableModuleVersionResolveResult rootModule = new DefaultBuildableModuleVersionResolveResult();
+        moduleResolver.resolve(configuration.getModule(), configuration.getAll(), rootModule);
 
-        ResolveState resolveState = new ResolveState(rootMetaData, configuration.getName(), dependencyResolver, resolveData);
+        ResolveState resolveState = new ResolveState(rootModule, configuration.getName(), dependencyResolver, dependencyToConfigurationResolver, configurationBuilder);
         traverseGraph(resolveState);
 
-        DefaultLenientConfiguration result = new DefaultLenientConfiguration(configuration, resolveState.root.getResult());
-        assembleResult(resolveState, result, listener);
-
-        return result;
+        assembleResult(resolveState, configurationBuilder, listener);
     }
 
     /**
      * Traverses the dependency graph, resolving conflicts and building the paths from the root configuration.
      */
     private void traverseGraph(ResolveState resolveState) {
-        Set<ModuleId> conflicts = new LinkedHashSet<ModuleId>();
+        Set<ModuleIdentifier> conflicts = new LinkedHashSet<ModuleIdentifier>();
 
         resolveState.onMoreSelected(resolveState.root);
 
@@ -96,20 +92,19 @@ public class DependencyGraphBuilder {
                     LOGGER.debug("Visiting dependency {}", dependency);
 
                     // Resolve dependency to a particular revision
-                    dependency.resolveModuleRevisionId();
-                    DefaultModuleRevisionResolveState moduleRevision = dependency.getTargetModuleRevision();
+                    ModuleVersionResolveState moduleRevision = dependency.resolveModuleRevisionId();
                     if (moduleRevision == null) {
                         // Failed to resolve.
                         continue;
                     }
-                    ModuleId moduleId= moduleRevision.id.getModuleId();
+                    ModuleIdentifier moduleId = moduleRevision.id.getModule();
 
                     // Check for a new conflict
                     if (moduleRevision.state == ModuleState.New) {
                         ModuleResolveState module = resolveState.getModule(moduleId);
 
                         // A new module revision. Check for conflict
-                        Collection<DefaultModuleRevisionResolveState> versions = module.getVersions();
+                        Collection<ModuleVersionResolveState> versions = module.getVersions();
                         if (versions.size() == 1) {
                             // First version of this module. Select it for now
                             LOGGER.debug("Selecting new module version {}", moduleRevision);
@@ -121,7 +116,7 @@ public class DependencyGraphBuilder {
 
                             // Deselect the currently selected version, and remove all outgoing edges from the version
                             // This will propagate through the graph and prune configurations that are no longer required
-                            DefaultModuleRevisionResolveState previouslySelected = module.clearSelection();
+                            ModuleVersionResolveState previouslySelected = module.clearSelection();
                             if (previouslySelected != null) {
                                 for (ConfigurationNode configuration : previouslySelected.configurations) {
                                     configuration.removeOutgoingEdges();
@@ -134,10 +129,10 @@ public class DependencyGraphBuilder {
                 }
             } else {
                 // We have some batched up conflicts. Resolve the first, and continue traversing the graph
-                ModuleId moduleId = conflicts.iterator().next();
+                ModuleIdentifier moduleId = conflicts.iterator().next();
                 conflicts.remove(moduleId);
                 ModuleResolveState module = resolveState.getModule(moduleId);
-                DefaultModuleRevisionResolveState selected = conflictResolver.select(module.getVersions(), resolveState.root.moduleRevision);
+                ModuleVersionResolveState selected = conflictResolver.select(module.getVersions(), resolveState.root.moduleRevision);
                 LOGGER.debug("Selected {} from conflicting modules {}.", selected, module.getVersions());
 
                 // Restart each configuration. For the evicted configuration, this means moving incoming dependencies across to the
@@ -155,19 +150,24 @@ public class DependencyGraphBuilder {
         ModuleVersionIdentifier root = resolveState.root.toId();
         listener.start(root);
 
+        // Visit the nodes
         for (ConfigurationNode resolvedConfiguration : resolveState.getConfigurationNodes()) {
             if (resolvedConfiguration.isSelected()) {
-                resolvedConfiguration.attachToParents(resolvedArtifactFactory, result);
+                resolvedConfiguration.validate();
+                result.newResolvedDependency(resolvedConfiguration.getResult());
                 resolvedConfiguration.collectFailures(failureState);
                 listener.resolvedModuleVersion(resolvedConfiguration.moduleRevision);
             }
         }
+        // Visit the edges
         for (ConfigurationNode resolvedConfiguration : resolveState.getConfigurationNodes()) {
             if (resolvedConfiguration.isSelected()) {
+                resolvedConfiguration.attachToParents(result);
                 listener.resolvedConfiguration(resolvedConfiguration.toId(), resolvedConfiguration.outgoingEdges);
             }
         }
         failureState.attachFailures(result);
+        result.done(resolveState.root.getResult());
     }
 
     private static class FailureState {
@@ -180,29 +180,29 @@ public class DependencyGraphBuilder {
 
         public void attachFailures(ResolvedConfigurationBuilder result) {
             for (Map.Entry<ModuleVersionSelector, BrokenDependency> entry : failuresByRevisionId.entrySet()) {
-                Collection<List<ModuleRevisionId>> paths = calculatePaths(entry.getValue());
+                Collection<List<ModuleVersionIdentifier>> paths = calculatePaths(entry.getValue());
                 result.addUnresolvedDependency(new DefaultUnresolvedDependency(entry.getKey(), entry.getValue().failure.withIncomingPaths(paths)));
             }
         }
 
-        private Collection<List<ModuleRevisionId>> calculatePaths(BrokenDependency brokenDependency) {
+        private Collection<List<ModuleVersionIdentifier>> calculatePaths(BrokenDependency brokenDependency) {
             // Include the shortest path from each version that has a direct dependency on the broken dependency, back to the root
             
-            Map<DefaultModuleRevisionResolveState, List<ModuleRevisionId>> shortestPaths = new LinkedHashMap<DefaultModuleRevisionResolveState, List<ModuleRevisionId>>();
-            List<ModuleRevisionId> rootPath = new ArrayList<ModuleRevisionId>();
+            Map<ModuleVersionResolveState, List<ModuleVersionIdentifier>> shortestPaths = new LinkedHashMap<ModuleVersionResolveState, List<ModuleVersionIdentifier>>();
+            List<ModuleVersionIdentifier> rootPath = new ArrayList<ModuleVersionIdentifier>();
             rootPath.add(root.moduleRevision.id);
             shortestPaths.put(root.moduleRevision, rootPath);
 
-            Set<DefaultModuleRevisionResolveState> directDependees = new LinkedHashSet<DefaultModuleRevisionResolveState>();
+            Set<ModuleVersionResolveState> directDependees = new LinkedHashSet<ModuleVersionResolveState>();
             for (ConfigurationNode node : brokenDependency.requiredBy) {
                 directDependees.add(node.moduleRevision);
             }
 
-            Set<DefaultModuleRevisionResolveState> seen = new HashSet<DefaultModuleRevisionResolveState>();
-            LinkedList<DefaultModuleRevisionResolveState> queue = new LinkedList<DefaultModuleRevisionResolveState>();
+            Set<ModuleVersionResolveState> seen = new HashSet<ModuleVersionResolveState>();
+            LinkedList<ModuleVersionResolveState> queue = new LinkedList<ModuleVersionResolveState>();
             queue.addAll(directDependees);
             while (!queue.isEmpty()) {
-                DefaultModuleRevisionResolveState version = queue.getFirst();
+                ModuleVersionResolveState version = queue.getFirst();
                 if (version == root.moduleRevision) {
                     queue.removeFirst();
                 } else if (seen.add(version)) {
@@ -213,10 +213,10 @@ public class DependencyGraphBuilder {
                     }
                 } else {
                     queue.remove();
-                    List<ModuleRevisionId> shortest = null;
+                    List<ModuleVersionIdentifier> shortest = null;
                     for (ConfigurationNode configuration : version.configurations) {
                         for (DependencyEdge dependencyEdge : configuration.incomingEdges) {
-                            List<ModuleRevisionId> candidate = shortestPaths.get(dependencyEdge.from.moduleRevision);
+                            List<ModuleVersionIdentifier> candidate = shortestPaths.get(dependencyEdge.from.moduleRevision);
                             if (candidate == null) {
                                 continue;
                             }
@@ -230,16 +230,16 @@ public class DependencyGraphBuilder {
                     if (shortest == null) {
                         continue;
                     }
-                    List<ModuleRevisionId> path = new ArrayList<ModuleRevisionId>();
+                    List<ModuleVersionIdentifier> path = new ArrayList<ModuleVersionIdentifier>();
                     path.addAll(shortest);
                     path.add(version.id);
                     shortestPaths.put(version, path);
                 }
             }
 
-            List<List<ModuleRevisionId>> paths = new ArrayList<List<ModuleRevisionId>>();
-            for (DefaultModuleRevisionResolveState version : directDependees) {
-                List<ModuleRevisionId> path = shortestPaths.get(version);
+            List<List<ModuleVersionIdentifier>> paths = new ArrayList<List<ModuleVersionIdentifier>>();
+            for (ModuleVersionResolveState version : directDependees) {
+                List<ModuleVersionIdentifier> path = shortestPaths.get(version);
                 paths.add(path);
             }
             return paths;
@@ -264,45 +264,46 @@ public class DependencyGraphBuilder {
         }
     }
 
+    /**
+     * Represents the edges in the dependency graph.
+     */
     private static class DependencyEdge implements InternalDependencyResult {
         private final ConfigurationNode from;
         private final DependencyDescriptor dependencyDescriptor;
         private final DependencyMetaData dependencyMetaData;
-        private final Set<String> targetConfigurationRules;
         private final ResolveState resolveState;
         private final ModuleVersionSpec selectorSpec;
         private final Set<ConfigurationNode> targetConfigurations = new LinkedHashSet<ConfigurationNode>();
-        private ModuleVersionSelectorResolveState selector;
-        private DefaultModuleRevisionResolveState targetModuleRevision;
+        private final ModuleVersionSelectorResolveState selector;
+        private ModuleVersionResolveState targetModuleRevision;
 
-        public DependencyEdge(ConfigurationNode from, DependencyMetaData dependencyMetaData, Set<String> targetConfigurationRules, ModuleVersionSpec selectorSpec, ResolveState resolveState) {
+        public DependencyEdge(ConfigurationNode from, DependencyMetaData dependencyMetaData, ModuleVersionSpec selectorSpec, ResolveState resolveState) {
             this.from = from;
             this.dependencyMetaData = dependencyMetaData;
             this.dependencyDescriptor = dependencyMetaData.getDescriptor();
-            this.targetConfigurationRules = targetConfigurationRules;
             this.selectorSpec = selectorSpec;
             this.resolveState = resolveState;
+            selector = resolveState.getSelector(dependencyMetaData);
         }
 
         @Override
         public String toString() {
-            return String.format("%s -> %s(%s)", from.toString(), dependencyMetaData.getRequested(), targetConfigurationRules);
-        }
-
-        public DefaultModuleRevisionResolveState getTargetModuleRevision() {
-            return targetModuleRevision;
+            return String.format("%s -> %s(%s)", from.toString(), dependencyMetaData.getRequested(), dependencyDescriptor);
         }
 
-        public void resolveModuleRevisionId() {
+        /**
+         * @return The resolved module version
+         */
+        public ModuleVersionResolveState resolveModuleRevisionId() {
             if (targetModuleRevision == null) {
-                selector = resolveState.getSelector(dependencyMetaData, dependencyDescriptor.getDependencyRevisionId());
                 targetModuleRevision = selector.resolveModuleRevisionId();
-                selector.module.addUnattachedDependency(this);
+                selector.getSelectedModule().addUnattachedDependency(this);
             }
+            return targetModuleRevision;
         }
 
         public boolean isTransitive() {
-            return from.isTransitive() && dependencyDescriptor.isTransitive();
+            return from.isTransitive() && dependencyMetaData.isTransitive();
         }
 
         public void attachToTargetConfigurations() {
@@ -314,7 +315,7 @@ public class DependencyGraphBuilder {
                 targetConfiguration.addIncomingEdge(this);
             }
             if (!targetConfigurations.isEmpty()) {
-                selector.module.removeUnattachedDependency(this);
+                selector.getSelectedModule().removeUnattachedDependency(this);
             }
         }
 
@@ -323,10 +324,12 @@ public class DependencyGraphBuilder {
                 targetConfiguration.removeIncomingEdge(this);
             }
             targetConfigurations.clear();
+            if (targetModuleRevision != null) {
+                selector.getSelectedModule().removeUnattachedDependency(this);
+            }
         }
 
-        public void restart(DefaultModuleRevisionResolveState selected) {
-            selector = selector.restart(selected);
+        public void restart(ModuleVersionResolveState selected) {
             targetModuleRevision = selected;
             attachToTargetConfigurations();
         }
@@ -339,121 +342,102 @@ public class DependencyGraphBuilder {
                 return;
             }
 
-            ModuleDescriptor targetDescriptor = targetModuleVersion.getDescriptor();
-            IvyNode node = new IvyNode(resolveState.resolveData, targetDescriptor);
-            Set<String> targets = new LinkedHashSet<String>();
-            for (String targetConfiguration : targetConfigurationRules) {
-                Collections.addAll(targets, node.getRealConfs(targetConfiguration));
-            }
-
-            for (String targetConfigurationName : targets) {
-                // TODO - this is the wrong spot for this check
-                if (targetDescriptor.getConfiguration(targetConfigurationName) == null) {
-                    throw new RuntimeException(String.format("Module version group:%s, module:%s, version:%s, configuration:%s declares a dependency on configuration '%s' which is not declared in the module descriptor for group:%s, module:%s, version:%s",
-                            from.moduleRevision.id.getOrganisation(), from.moduleRevision.id.getName(), from.moduleRevision.id.getRevision(), from.configurationName,
-                            targetConfigurationName, targetModuleRevision.id.getOrganisation(), targetModuleRevision.id.getName(), targetModuleRevision.id.getRevision()));
-                }
-                ConfigurationNode targetConfiguration = resolveState.getConfigurationNode(targetModuleRevision, targetConfigurationName);
-                targetConfigurations.add(targetConfiguration);
+            Set<ConfigurationMetaData> targetConfigurations = resolveState.dependencyToConfigurationResolver.resolveTargetConfigurations(dependencyMetaData, from.metaData, targetModuleVersion);
+            for (ConfigurationMetaData targetConfiguration : targetConfigurations) {
+                ConfigurationNode targetConfigurationNode = resolveState.getConfigurationNode(targetModuleRevision, targetConfiguration.getName());
+                this.targetConfigurations.add(targetConfigurationNode);
             }
         }
 
-        private Set<ResolvedArtifact> getArtifacts(ConfigurationNode childConfiguration, ResolvedArtifactFactory resolvedArtifactFactory) {
-            String[] targetConfigurations = from.heirarchy.toArray(new String[from.heirarchy.size()]);
+        private Set<ResolvedArtifact> getArtifacts(ConfigurationNode childConfiguration) {
+            String[] targetConfigurations = from.metaData.getHierarchy().toArray(new String[from.metaData.getHierarchy().size()]);
             DependencyArtifactDescriptor[] dependencyArtifacts = dependencyDescriptor.getDependencyArtifacts(targetConfigurations);
             if (dependencyArtifacts.length == 0) {
                 return Collections.emptySet();
             }
             Set<ResolvedArtifact> artifacts = new LinkedHashSet<ResolvedArtifact>();
             for (DependencyArtifactDescriptor artifactDescriptor : dependencyArtifacts) {
-                MDArtifact artifact = new MDArtifact(childConfiguration.descriptor, artifactDescriptor.getName(), artifactDescriptor.getType(), artifactDescriptor.getExt(), artifactDescriptor.getUrl(), artifactDescriptor.getQualifiedExtraAttributes());
-                artifacts.add(resolvedArtifactFactory.create(childConfiguration.getResult(), artifact, selector.resolve().getArtifactResolver()));
+                ModuleRevisionId id = childConfiguration.moduleRevision.metaData.getDescriptor().getModuleRevisionId();
+                Artifact artifact = new DefaultArtifact(id, null, artifactDescriptor.getName(), artifactDescriptor.getType(), artifactDescriptor.getExt(), artifactDescriptor.getUrl(), artifactDescriptor.getQualifiedExtraAttributes());
+                artifacts.add(resolveState.builder.newArtifact(childConfiguration.getResult(), artifact, targetModuleRevision.resolve().getArtifactResolver()));
             }
             return artifacts;
         }
 
-        public void attachToParents(ConfigurationNode childConfiguration, ResolvedArtifactFactory artifactFactory, ResolvedConfigurationBuilder result) {
-            DefaultResolvedDependency parent = from.getResult();
-            DefaultResolvedDependency child = childConfiguration.getResult();
-            parent.addChild(child);
-
-            Set<ResolvedArtifact> artifacts = getArtifacts(childConfiguration, artifactFactory);
-            if (!artifacts.isEmpty()) {
-                child.addParentSpecificArtifacts(parent, artifacts);
-            }
+        public void attachToParents(ConfigurationNode childConfiguration, ResolvedConfigurationBuilder result) {
+            ResolvedConfigurationIdentifier parent = from.getResult();
+            ResolvedConfigurationIdentifier child = childConfiguration.getResult();
+            result.addChild(parent, child);
 
+            Set<ResolvedArtifact> artifacts = getArtifacts(childConfiguration);
             if (artifacts.isEmpty()) {
-                child.addParentSpecificArtifacts(parent, childConfiguration.getArtifacts(artifactFactory));
-            }
-            for (ResolvedArtifact artifact : child.getParentArtifacts(parent)) {
-                result.addArtifact(artifact);
+                artifacts = childConfiguration.getArtifacts();
             }
+            //TODO SF merge with addChild
+            result.addParentSpecificArtifacts(child, parent, artifacts);
 
-            if (parent == result.getRoot()) {
+            if (parent == resolveState.root.getResult()) {
                 EnhancedDependencyDescriptor enhancedDependencyDescriptor = (EnhancedDependencyDescriptor) dependencyDescriptor;
                 result.addFirstLevelDependency(enhancedDependencyDescriptor.getModuleDependency(), child);
             }
         }
 
         public ModuleVersionSpec getSelector() {
-            String[] configurations = from.heirarchy.toArray(new String[from.heirarchy.size()]);
+            String[] configurations = from.metaData.getHierarchy().toArray(new String[from.metaData.getHierarchy().size()]);
             ModuleVersionSpec selector = ModuleVersionSpec.forExcludes(dependencyDescriptor.getExcludeRules(configurations));
             return selector.intersect(selectorSpec);
         }
 
-        public boolean isFailed() {
-            return selector != null && selector.failure != null;
-        }
-
         public ModuleVersionSelector getRequested() {
             return dependencyMetaData.getRequested();
         }
 
         public ModuleVersionResolveException getFailure() {
-            //see also getSelected(). For evicted targetModuleRevisions, we need to reach out to the failure of selected module
-            //covered in VersionConflictResolutionIntegrationTest
-            return selector.failure != null ? selector.failure : getSelected().resolver.failure;
+            return selector.getFailure();
         }
 
-        public DefaultModuleRevisionResolveState getSelected() {
-            //we cannot use the targetModuleRevision field because it may have been evicted
-            //covered in VersionConflictResolutionIntegrationTest
-            return selector.module.selected;
+        public ModuleVersionResolveState getSelected() {
+            return selector.getSelected();
         }
 
         public ModuleVersionSelectionReason getReason() {
-            return getSelected() == null ? selector.idSelectionReason : getSelected().selectionReason;
+            return selector.getSelectionReason();
         }
 
         public void collectFailures(FailureState failureState) {
-            if (isFailed()) {
-                failureState.addUnresolvedDependency(this, selector.dependencyMetaData.getRequested(), selector.failure);
+            ModuleVersionResolveException failure = getFailure();
+            if (failure != null) {
+                failureState.addUnresolvedDependency(this, selector.dependencyMetaData.getRequested(), failure);
             }
         }
-
     }
 
+    /**
+     * Global resolution state.
+     */
     private static class ResolveState {
-        private final Map<ModuleId, ModuleResolveState> modules = new LinkedHashMap<ModuleId, ModuleResolveState>();
+        private final Map<ModuleIdentifier, ModuleResolveState> modules = new LinkedHashMap<ModuleIdentifier, ModuleResolveState>();
         private final Map<ResolvedConfigurationIdentifier, ConfigurationNode> nodes = new LinkedHashMap<ResolvedConfigurationIdentifier, ConfigurationNode>();
-        private final Map<ModuleRevisionId, ModuleVersionSelectorResolveState> selectors = new LinkedHashMap<ModuleRevisionId, ModuleVersionSelectorResolveState>();
+        private final Map<ModuleVersionSelector, ModuleVersionSelectorResolveState> selectors = new LinkedHashMap<ModuleVersionSelector, ModuleVersionSelectorResolveState>();
         private final ConfigurationNode root;
         private final DependencyToModuleVersionIdResolver resolver;
-        private final ResolveData resolveData;
+        private final DependencyToConfigurationResolver dependencyToConfigurationResolver;
+        private final ResolvedConfigurationBuilder builder;
         private final Set<ConfigurationNode> queued = new HashSet<ConfigurationNode>();
         private final LinkedList<ConfigurationNode> queue = new LinkedList<ConfigurationNode>();
 
-        public ResolveState(BuildableModuleVersionMetaData rootModule, String rootConfigurationName, DependencyToModuleVersionIdResolver resolver, ResolveData resolveData) {
+        public ResolveState(ModuleVersionResolveResult rootResult, String rootConfigurationName, DependencyToModuleVersionIdResolver resolver,
+                            DependencyToConfigurationResolver dependencyToConfigurationResolver, ResolvedConfigurationBuilder builder) {
             this.resolver = resolver;
-            this.resolveData = resolveData;
-            DefaultModuleRevisionResolveState rootVersion = getRevision(rootModule.getId());
-            rootVersion.setMetaData(rootModule);
+            this.dependencyToConfigurationResolver = dependencyToConfigurationResolver;
+            this.builder = builder;
+            ModuleVersionResolveState rootVersion = getRevision(rootResult.getId());
+            rootVersion.setResolveResult(rootResult);
             root = getConfigurationNode(rootVersion, rootConfigurationName);
             root.moduleRevision.module.select(root.moduleRevision);
         }
 
-        public ModuleResolveState getModule(ModuleId moduleId) {
-            ModuleId id = new ModuleId(moduleId.getOrganisation(), moduleId.getName());
+        public ModuleResolveState getModule(ModuleIdentifier id) {
             ModuleResolveState module = modules.get(id);
             if (module == null) {
                 module = new ModuleResolveState(id, this);
@@ -462,32 +446,31 @@ public class DependencyGraphBuilder {
             return module;
         }
 
-        public DefaultModuleRevisionResolveState getRevision(ModuleVersionIdentifier moduleVersionIdentifier) {
-            ModuleRevisionId id = new ModuleRevisionId(new ModuleId(moduleVersionIdentifier.getGroup(), moduleVersionIdentifier.getName()), moduleVersionIdentifier.getVersion());
-            return getModule(id.getModuleId()).getVersion(id);
+        public ModuleVersionResolveState getRevision(ModuleVersionIdentifier id) {
+            return getModule(id.getModule()).getVersion(id);
         }
 
         public Collection<ConfigurationNode> getConfigurationNodes() {
             return nodes.values();
         }
 
-        public ConfigurationNode getConfigurationNode(DefaultModuleRevisionResolveState module, String configurationName) {
-            ModuleRevisionId original = module.id;
-            ResolvedConfigurationIdentifier id = new ResolvedConfigurationIdentifier(original.getOrganisation(), original.getName(), original.getRevision(), configurationName);
+        public ConfigurationNode getConfigurationNode(ModuleVersionResolveState module, String configurationName) {
+            ModuleVersionIdentifier original = module.id;
+            ResolvedConfigurationIdentifier id = new ResolvedConfigurationIdentifier(original, configurationName);
             ConfigurationNode configuration = nodes.get(id);
             if (configuration == null) {
-                configuration = new ConfigurationNode(module, module.metaData, configurationName, this);
+                configuration = new ConfigurationNode(module, configurationName, this);
                 nodes.put(id, configuration);
             }
             return configuration;
         }
 
-        public ModuleVersionSelectorResolveState getSelector(DependencyMetaData dependencyMetaData, ModuleRevisionId original) {
-            ModuleRevisionId selectorId = ModuleRevisionId.newInstance(original.getOrganisation(), original.getName(), original.getRevision());
-            ModuleVersionSelectorResolveState resolveState = selectors.get(selectorId);
+        public ModuleVersionSelectorResolveState getSelector(DependencyMetaData dependencyMetaData) {
+            ModuleVersionSelector requested = dependencyMetaData.getRequested();
+            ModuleVersionSelectorResolveState resolveState = selectors.get(requested);
             if (resolveState == null) {
-                resolveState = new ModuleVersionSelectorResolveState(dependencyMetaData, getModule(selectorId.getModuleId()), resolver, this);
-                selectors.put(selectorId, resolveState);
+                resolveState = new ModuleVersionSelectorResolveState(dependencyMetaData, resolver, this);
+                selectors.put(requested, resolveState);
             }
             return resolveState;
         }
@@ -531,45 +514,57 @@ public class DependencyGraphBuilder {
         Evicted
     }
 
+    /**
+     * Resolution state for a given module.
+     */
     private static class ModuleResolveState {
-        final ModuleId id;
+        final ModuleIdentifier id;
         final Set<DependencyEdge> unattachedDependencies = new LinkedHashSet<DependencyEdge>();
-        final Map<ModuleRevisionId, DefaultModuleRevisionResolveState> versions = new LinkedHashMap<ModuleRevisionId, DefaultModuleRevisionResolveState>();
+        final Map<ModuleVersionIdentifier, ModuleVersionResolveState> versions = new LinkedHashMap<ModuleVersionIdentifier, ModuleVersionResolveState>();
+        final Set<ModuleVersionSelectorResolveState> selectors = new HashSet<ModuleVersionSelectorResolveState>();
         final ResolveState resolveState;
-        DefaultModuleRevisionResolveState selected;
+        ModuleVersionResolveState selected;
 
-        private ModuleResolveState(ModuleId id, ResolveState resolveState) {
+        private ModuleResolveState(ModuleIdentifier id, ResolveState resolveState) {
             this.id = id;
             this.resolveState = resolveState;
         }
 
-        public Collection<DefaultModuleRevisionResolveState> getVersions() {
+        @Override
+        public String toString() {
+            return id.toString();
+        }
+
+        public Collection<ModuleVersionResolveState> getVersions() {
             return versions.values();
         }
 
-        public void select(DefaultModuleRevisionResolveState selected) {
+        public void select(ModuleVersionResolveState selected) {
             assert this.selected == null;
             this.selected = selected;
-            for (DefaultModuleRevisionResolveState version : versions.values()) {
+            for (ModuleVersionResolveState version : versions.values()) {
                 version.state = ModuleState.Evicted;
             }
             selected.state = ModuleState.Selected;
         }
 
-        public DefaultModuleRevisionResolveState clearSelection() {
-            DefaultModuleRevisionResolveState previousSelection = selected;
+        public ModuleVersionResolveState clearSelection() {
+            ModuleVersionResolveState previousSelection = selected;
             selected = null;
-            for (DefaultModuleRevisionResolveState version : versions.values()) {
+            for (ModuleVersionResolveState version : versions.values()) {
                 version.state = ModuleState.Conflict;
             }
             return previousSelection;
         }
 
-        public void restart(DefaultModuleRevisionResolveState selected) {
+        public void restart(ModuleVersionResolveState selected) {
             select(selected);
-            for (DefaultModuleRevisionResolveState version : versions.values()) {
+            for (ModuleVersionResolveState version : versions.values()) {
                 version.restart(selected);
             }
+            for (ModuleVersionSelectorResolveState selector : selectors) {
+                selector.restart(selected);
+            }
             for (DependencyEdge dependency : new ArrayList<DependencyEdge>(unattachedDependencies)) {
                 dependency.restart(selected);
             }
@@ -584,29 +579,37 @@ public class DependencyGraphBuilder {
             unattachedDependencies.remove(edge);
         }
 
-        public DefaultModuleRevisionResolveState getVersion(ModuleRevisionId id) {
-            DefaultModuleRevisionResolveState moduleRevision = versions.get(id);
+        public ModuleVersionResolveState getVersion(ModuleVersionIdentifier id) {
+            ModuleVersionResolveState moduleRevision = versions.get(id);
             if (moduleRevision == null) {
-                moduleRevision = new DefaultModuleRevisionResolveState(this, id, resolveState);
+                moduleRevision = new ModuleVersionResolveState(this, id, resolveState);
                 versions.put(id, moduleRevision);
             }
 
             return moduleRevision;
         }
+
+        public void addSelector(ModuleVersionSelectorResolveState selector) {
+            selectors.add(selector);
+        }
     }
 
-    private static class DefaultModuleRevisionResolveState implements ModuleRevisionResolveState, ModuleVersionSelection {
+    /**
+     * Resolution state for a given module version.
+     */
+    private static class ModuleVersionResolveState implements ModuleRevisionResolveState, ModuleVersionSelection {
         final ModuleResolveState module;
-        final ModuleRevisionId id;
+        final ModuleVersionIdentifier id;
         final ResolveState resolveState;
         final Set<ConfigurationNode> configurations = new LinkedHashSet<ConfigurationNode>();
-        List<DependencyMetaData> dependencies;
         ModuleVersionMetaData metaData;
         ModuleState state = ModuleState.New;
-        ModuleVersionSelectorResolveState resolver;
         ModuleVersionSelectionReason selectionReason = VersionSelectionReasons.REQUESTED;
+        ModuleVersionIdResolveResult idResolveResult;
+        ModuleVersionResolveResult resolveResult;
+        ModuleVersionResolveException failure;
 
-        private DefaultModuleRevisionResolveState(ModuleResolveState module, ModuleRevisionId id, ResolveState resolveState) {
+        private ModuleVersionResolveState(ModuleResolveState module, ModuleVersionIdentifier id, ResolveState resolveState) {
             this.module = module;
             this.id = id;
             this.resolveState = resolveState;
@@ -617,47 +620,58 @@ public class DependencyGraphBuilder {
             return id.toString();
         }
 
-        public String getRevision() {
-            return id.getRevision();
+        public String getVersion() {
+            return id.getVersion();
         }
 
-        public Iterable<DependencyMetaData> getDependencies() {
-            if (dependencies == null) {
-                dependencies = getMetaData().getDependencies();
-            }
-            return dependencies;
+        public String getId() {
+            return String.format("%s:%s:%s", id.getGroup(), id.getName(), id.getVersion());
         }
 
-        public String getId() {
-            return String.format("%s:%s:%s", id.getOrganisation(), id.getName(), id.getRevision());
+        public ModuleVersionResolveException getFailure() {
+            return failure;
         }
 
-        public void restart(DefaultModuleRevisionResolveState selected) {
-            for (ConfigurationNode conflictConfiguration : configurations) {
-                conflictConfiguration.restart(selected);
+        public void restart(ModuleVersionResolveState selected) {
+            for (ConfigurationNode configuration : configurations) {
+                configuration.restart(selected);
             }
         }
 
         public void addResolver(ModuleVersionSelectorResolveState resolver) {
-            if (this.resolver == null) {
-                this.resolver = resolver;
+            if (this.idResolveResult == null) {
+                idResolveResult = resolver.idResolveResult;
+            }
+        }
+
+        public ModuleVersionResolveResult resolve() {
+            if (resolveResult != null) {
+                return resolveResult;
+            }
+            if (failure != null) {
+                return null;
+            }
+
+            resolveResult = idResolveResult.resolve();
+            if (resolveResult.getFailure() != null) {
+                failure = resolveResult.getFailure();
+                return null;
             }
+            setResolveResult(resolveResult);
+            return resolveResult;
         }
 
         public ModuleVersionMetaData getMetaData() {
             if (metaData == null) {
-                if (resolver == null) {
-                    throw new IllegalStateException(String.format("No resolver for %s.", this));
-                }
-                resolver.resolve();
+                resolve();
             }
             return metaData;
         }
 
-        public void setMetaData(ModuleVersionMetaData metaData) {
-            if (this.metaData == null) {
-                this.metaData = metaData;
-            }
+        public void setResolveResult(ModuleVersionResolveResult resolveResult) {
+            this.resolveResult = resolveResult;
+            this.metaData = resolveResult.getMetaData();
+            this.failure = null;
         }
 
         public void addConfiguration(ConfigurationNode configurationNode) {
@@ -665,10 +679,7 @@ public class DependencyGraphBuilder {
         }
 
         public ModuleVersionIdentifier getSelectedId() {
-            return new DefaultModuleVersionIdentifier(
-                    id.getOrganisation(),
-                    id.getName(),
-                    id.getRevision());
+            return id;
         }
 
         public ModuleVersionSelectionReason getSelectionReason() {
@@ -680,65 +691,48 @@ public class DependencyGraphBuilder {
         }
     }
 
+    /**
+     * Represents a node in the dependency graph.
+     */
     private static class ConfigurationNode {
-        final DefaultModuleRevisionResolveState moduleRevision;
+        final ModuleVersionResolveState moduleRevision;
+        final ConfigurationMetaData metaData;
         final ResolveState resolveState;
-        final DefaultModuleDescriptor descriptor;
-        final String configurationName;
-        final Set<String> heirarchy = new LinkedHashSet<String>();
         final Set<DependencyEdge> incomingEdges = new LinkedHashSet<DependencyEdge>();
         final Set<DependencyEdge> outgoingEdges = new LinkedHashSet<DependencyEdge>();
-        DefaultResolvedDependency result;
+        final ResolvedConfigurationIdentifier result;
         ModuleVersionSpec previousTraversal;
         Set<ResolvedArtifact> artifacts;
 
-        private ConfigurationNode(DefaultModuleRevisionResolveState moduleRevision, ModuleVersionMetaData moduleVersionMetaData, String configurationName, ResolveState resolveState) {
+        private ConfigurationNode(ModuleVersionResolveState moduleRevision, String configurationName, ResolveState resolveState) {
             this.moduleRevision = moduleRevision;
             this.resolveState = resolveState;
-            this.descriptor = (DefaultModuleDescriptor) moduleVersionMetaData.getDescriptor();
-            this.configurationName = configurationName;
-            findAncestors(configurationName, resolveState, heirarchy);
+            this.metaData = moduleRevision.metaData.getConfiguration(configurationName);
+            result = new ResolvedConfigurationIdentifier(moduleRevision.id, configurationName);
             moduleRevision.addConfiguration(this);
         }
 
-        void findAncestors(String config, ResolveState container, Set<String> ancestors) {
-            ancestors.add(config);
-            for (String parentConfig : descriptor.getConfiguration(config).getExtends()) {
-                ancestors.addAll(container.getConfigurationNode(moduleRevision, parentConfig).heirarchy);
-            }
-        }
-
         @Override
         public String toString() {
-            return String.format("%s(%s)", moduleRevision, configurationName);
+            return String.format("%s(%s)", moduleRevision, metaData.getName());
         }
 
-        public Set<ResolvedArtifact> getArtifacts(ResolvedArtifactFactory resolvedArtifactFactory) {
+        public Set<ResolvedArtifact> getArtifacts() {
             if (artifacts == null) {
                 artifacts = new LinkedHashSet<ResolvedArtifact>();
-                for (String config : heirarchy) {
-                    for (Artifact artifact : descriptor.getArtifacts(config)) {
-                        artifacts.add(resolvedArtifactFactory.create(getResult(), artifact, moduleRevision.resolver.resolve().getArtifactResolver()));
-                    }
+                for (Artifact artifact : metaData.getArtifacts()) {
+                    artifacts.add(resolveState.builder.newArtifact(getResult(), artifact, moduleRevision.resolve().getArtifactResolver()));
                 }
             }
             return artifacts;
         }
 
-        public DefaultResolvedDependency getResult() {
-            if (result == null) {
-                result = new DefaultResolvedDependency(
-                        moduleRevision.id.getOrganisation(),
-                        moduleRevision.id.getName(),
-                        moduleRevision.id.getRevision(),
-                        configurationName);
-            }
-
+        public ResolvedConfigurationIdentifier getResult() {
             return result;
         }
 
         public boolean isTransitive() {
-            return descriptor.getConfiguration(configurationName).isTransitive();
+            return metaData.isTransitive();
         }
 
         public void visitOutgoingDependencies(Collection<DependencyEdge> target) {
@@ -747,7 +741,7 @@ public class DependencyGraphBuilder {
             // If traversed before, and the selected modules have changed, remove previous outgoing edges and add outgoing edges again with
             //    the new selections.
             // If traversed before, and the selected modules have not changed, ignore
-            // If none of the incoming edges is transitive, then the node has no outgoing edges
+            // If none of the incoming edges are transitive, then the node has no outgoing edges
 
             if (moduleRevision.state != ModuleState.Selected) {
                 LOGGER.debug("version for {} is not selected. ignoring.", this);
@@ -782,35 +776,20 @@ public class DependencyGraphBuilder {
                 removeOutgoingEdges();
             }
 
-            for (DependencyMetaData dependency : moduleRevision.getDependencies()) {
+            for (DependencyMetaData dependency : metaData.getDependencies()) {
                 DependencyDescriptor dependencyDescriptor = dependency.getDescriptor();
                 ModuleId targetModuleId = dependencyDescriptor.getDependencyRevisionId().getModuleId();
-                Set<String> targetConfigurations = getTargetConfigurations(dependencyDescriptor);
-                if (!targetConfigurations.isEmpty()) {
-                    if (!selectorSpec.isSatisfiedBy(targetModuleId)) {
-                        LOGGER.debug("{} is excluded from {}.", targetModuleId, this);
-                    } else {
-                        DependencyEdge dependencyEdge = new DependencyEdge(this, dependency, targetConfigurations, selectorSpec, resolveState);
-                        outgoingEdges.add(dependencyEdge);
-                        target.add(dependencyEdge);
-                    }
+                if (!selectorSpec.isSatisfiedBy(targetModuleId)) {
+                    LOGGER.debug("{} is excluded from {}.", targetModuleId, this);
+                    continue;
                 }
+                DependencyEdge dependencyEdge = new DependencyEdge(this, dependency, selectorSpec, resolveState);
+                outgoingEdges.add(dependencyEdge);
+                target.add(dependencyEdge);
             }
             previousTraversal = selectorSpec;
         }
 
-        Set<String> getTargetConfigurations(DependencyDescriptor dependencyDescriptor) {
-            Set<String> targetConfigurations = new LinkedHashSet<String>();
-            for (String moduleConfiguration : dependencyDescriptor.getModuleConfigurations()) {
-                if (moduleConfiguration.equals("*") || heirarchy.contains(moduleConfiguration)) {
-                    for (String targetConfiguration : dependencyDescriptor.getDependencyConfigurations(moduleConfiguration)) {
-                        targetConfigurations.add(targetConfiguration);
-                    }
-                }
-            }
-            return targetConfigurations;
-        }
-
         public void addIncomingEdge(DependencyEdge dependencyEdge) {
             incomingEdges.add(dependencyEdge);
             resolveState.onMoreSelected(this);
@@ -825,10 +804,10 @@ public class DependencyGraphBuilder {
             return moduleRevision.state == ModuleState.Selected;
         }
 
-        public void attachToParents(ResolvedArtifactFactory artifactFactory, ResolvedConfigurationBuilder result) {
+        public void attachToParents(ResolvedConfigurationBuilder result) {
             LOGGER.debug("Attaching {} to its parents.", this);
             for (DependencyEdge dependency : incomingEdges) {
-                dependency.attachToParents(this, artifactFactory, result);
+                dependency.attachToParents(this, result);
             }
         }
 
@@ -849,8 +828,7 @@ public class DependencyGraphBuilder {
                     selector = selector.union(dependencyEdge.getSelector());
                 }
             }
-            String[] configurations = heirarchy.toArray(new String[heirarchy.size()]);
-            selector = selector.intersect(ModuleVersionSpec.forExcludes(descriptor.getExcludeRules(configurations)));
+            selector = selector.intersect(ModuleVersionSpec.forExcludes(metaData.getExcludeRules()));
             return selector;
         }
 
@@ -862,43 +840,52 @@ public class DependencyGraphBuilder {
             previousTraversal = null;
         }
 
-        public void restart(DefaultModuleRevisionResolveState state) {
+        public void restart(ModuleVersionResolveState selected) {
             // Restarting this configuration after conflict resolution.
             // If this configuration belongs to the select version, queue ourselves up for traversal.
             // If not, then move our incoming edges across to the selected configuration
-            if (moduleRevision == state) {
+            if (moduleRevision == selected) {
                 resolveState.onMoreSelected(this);
             } else {
                 for (DependencyEdge dependency : incomingEdges) {
-                    dependency.restart(state);
+                    dependency.restart(selected);
                 }
                 incomingEdges.clear();
             }
         }
 
         private ModuleVersionIdentifier toId() {
-            return newId(moduleRevision.id.getOrganisation(),
-                    moduleRevision.id.getName(),
-                    moduleRevision.id.getRevision());
+            return moduleRevision.id;
+        }
+
+        // TODO:ADAM - remove this
+        public void validate() {
+            for (DependencyEdge incomingEdge : incomingEdges) {
+                ModuleState state = incomingEdge.from.moduleRevision.state;
+                if (state != ModuleState.Selected) {
+                    throw new IllegalStateException(String.format("Unexpected state %s for parent node for dependency from %s to %s.", state, incomingEdge.from, this));
+                }
+            }
         }
     }
 
+    /**
+     * Resolution state for a given module version selector.
+     */
     private static class ModuleVersionSelectorResolveState {
         final DependencyToModuleVersionIdResolver resolver;
         final ResolveState resolveState;
         final DependencyMetaData dependencyMetaData;
-        ModuleResolveState module;
         ModuleVersionResolveException failure;
-        ModuleVersionSelectionReason idSelectionReason;
-        DefaultModuleRevisionResolveState targetModuleRevision;
+        ModuleResolveState targetModule;
+        ModuleVersionResolveState targetModuleRevision;
         ModuleVersionIdResolveResult idResolveResult;
-        ModuleVersionResolveResult resolveResult;
 
-        private ModuleVersionSelectorResolveState(DependencyMetaData dependencyMetaData, ModuleResolveState module, DependencyToModuleVersionIdResolver resolver, ResolveState resolveState) {
+        private ModuleVersionSelectorResolveState(DependencyMetaData dependencyMetaData, DependencyToModuleVersionIdResolver resolver, ResolveState resolveState) {
             this.dependencyMetaData = dependencyMetaData;
-            this.module = module;
             this.resolver = resolver;
             this.resolveState = resolveState;
+            targetModule = resolveState.getModule(new DefaultModuleIdentifier(dependencyMetaData.getRequested().getGroup(), dependencyMetaData.getRequested().getName()));
         }
 
         @Override
@@ -906,10 +893,26 @@ public class DependencyGraphBuilder {
             return dependencyMetaData.toString();
         }
 
+        private ModuleVersionResolveException getFailure() {
+            return failure != null ? failure : targetModuleRevision.getFailure();
+        }
+
+        public ModuleVersionSelectionReason getSelectionReason() {
+            return targetModuleRevision == null ? idResolveResult.getSelectionReason() : targetModuleRevision.getSelectionReason();
+        }
+
+        public ModuleVersionResolveState getSelected() {
+            return targetModule.selected;
+        }
+
+        public ModuleResolveState getSelectedModule() {
+            return targetModule;
+        }
+
         /**
          * @return The module version, or null if there is a failure to resolve this selector.
          */
-        public DefaultModuleRevisionResolveState resolveModuleRevisionId() {
+        public ModuleVersionResolveState resolveModuleRevisionId() {
             if (targetModuleRevision != null) {
                 return targetModuleRevision;
             }
@@ -918,7 +921,6 @@ public class DependencyGraphBuilder {
             }
 
             idResolveResult = resolver.resolve(dependencyMetaData);
-            idSelectionReason = idResolveResult.getSelectionReason();
             if (idResolveResult.getFailure() != null) {
                 failure = idResolveResult.getFailure();
                 return null;
@@ -927,33 +929,15 @@ public class DependencyGraphBuilder {
             targetModuleRevision = resolveState.getRevision(idResolveResult.getId());
             targetModuleRevision.addResolver(this);
             targetModuleRevision.selectionReason = idResolveResult.getSelectionReason();
-
-            //the target module details might have been substituted / forced when resolving ID
-            //so update the module:
-            this.module = targetModuleRevision.module;
+            targetModule = targetModuleRevision.module;
+            targetModule.addSelector(this);
 
             return targetModuleRevision;
         }
 
-        public ModuleVersionResolveResult resolve() {
-            if (resolveResult != null) {
-                return resolveResult;
-            }
-            if (failure != null) {
-                return null;
-            }
-
-            try {
-                resolveResult = idResolveResult.resolve();
-                resolveState.getRevision(resolveResult.getId()).setMetaData(resolveResult.getMetaData());
-            } catch (ModuleVersionResolveException e) {
-                failure = e;
-            }
-            return resolveResult;
-        }
-
-        public ModuleVersionSelectorResolveState restart(DefaultModuleRevisionResolveState moduleRevision) {
-            return resolveState.getSelector(dependencyMetaData.withRequestedVersion(moduleRevision.id.getRevision()), moduleRevision.id);
+        public void restart(ModuleVersionResolveState moduleRevision) {
+            this.targetModuleRevision = moduleRevision;
+            this.targetModule = moduleRevision.module;
         }
     }
 
@@ -964,7 +948,7 @@ public class DependencyGraphBuilder {
             this.resolver = resolver;
         }
 
-        DefaultModuleRevisionResolveState select(Collection<DefaultModuleRevisionResolveState> candidates, DefaultModuleRevisionResolveState root) {
+        ModuleVersionResolveState select(Collection<ModuleVersionResolveState> candidates, ModuleVersionResolveState root) {
             for (ConfigurationNode configuration : root.configurations) {
                 for (DependencyEdge outgoingEdge : configuration.outgoingEdges) {
                     if (outgoingEdge.dependencyDescriptor.isForce() && candidates.contains(outgoingEdge.targetModuleRevision)) {
@@ -973,7 +957,7 @@ public class DependencyGraphBuilder {
                     }
                 }
             }
-            return (DefaultModuleRevisionResolveState) resolver.select(candidates, root);
+            return (ModuleVersionResolveState) resolver.select(candidates);
         }
     }
 }
\ No newline at end of file
diff --git a/subprojects/core-impl/src/main/groovy/org/gradle/api/internal/artifacts/ivyservice/resolveengine/DependencyToConfigurationResolver.java b/subprojects/core-impl/src/main/groovy/org/gradle/api/internal/artifacts/ivyservice/resolveengine/DependencyToConfigurationResolver.java
new file mode 100644
index 0000000..b1fc361
--- /dev/null
+++ b/subprojects/core-impl/src/main/groovy/org/gradle/api/internal/artifacts/ivyservice/resolveengine/DependencyToConfigurationResolver.java
@@ -0,0 +1,30 @@
+/*
+ * Copyright 2013 the original author or authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY 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.resolveengine;
+
+import org.gradle.api.internal.artifacts.ivyservice.ivyresolve.ConfigurationMetaData;
+import org.gradle.api.internal.artifacts.ivyservice.ivyresolve.DependencyMetaData;
+import org.gradle.api.internal.artifacts.ivyservice.ivyresolve.ModuleVersionMetaData;
+
+import java.util.Set;
+
+/**
+ * Responsible for mapping a dependency definition to the set of configurations that it refers to.
+ */
+public interface DependencyToConfigurationResolver {
+    Set<ConfigurationMetaData> resolveTargetConfigurations(DependencyMetaData dependencyMetaData, ConfigurationMetaData fromConfiguration, ModuleVersionMetaData targetModuleVersion);
+}
diff --git a/subprojects/core-impl/src/main/groovy/org/gradle/api/internal/artifacts/ivyservice/resolveengine/LatestModuleConflictResolver.java b/subprojects/core-impl/src/main/groovy/org/gradle/api/internal/artifacts/ivyservice/resolveengine/LatestModuleConflictResolver.java
index 14be289..17cfbc2 100644
--- a/subprojects/core-impl/src/main/groovy/org/gradle/api/internal/artifacts/ivyservice/resolveengine/LatestModuleConflictResolver.java
+++ b/subprojects/core-impl/src/main/groovy/org/gradle/api/internal/artifacts/ivyservice/resolveengine/LatestModuleConflictResolver.java
@@ -15,23 +15,18 @@
  */
 package org.gradle.api.internal.artifacts.ivyservice.resolveengine;
 
-import org.gradle.api.internal.artifacts.version.LatestVersionSemanticComparator;
+import org.gradle.api.internal.artifacts.ivyservice.ivyresolve.strategy.LatestStrategy;
 
 import java.util.Collection;
-import java.util.Collections;
-import java.util.Comparator;
 
 class LatestModuleConflictResolver implements ModuleConflictResolver {
-    public ModuleRevisionResolveState select(Collection<? extends ModuleRevisionResolveState> candidates, ModuleRevisionResolveState root) {
-        return Collections.max(candidates, new VersionComparator());
-    }
-
-    private class VersionComparator implements Comparator<ModuleRevisionResolveState> {
+    private final LatestStrategy latestStrategy;
 
-        LatestVersionSemanticComparator delegate = new LatestVersionSemanticComparator();
+    LatestModuleConflictResolver(LatestStrategy latestStrategy) {
+        this.latestStrategy = latestStrategy;
+    }
 
-        public int compare(ModuleRevisionResolveState left, ModuleRevisionResolveState right) {
-            return delegate.compare(left.getRevision(), right.getRevision());
-        }
+    public ModuleRevisionResolveState select(Collection<? extends ModuleRevisionResolveState> candidates) {
+        return latestStrategy.findLatest(candidates);
     }
 }
diff --git a/subprojects/core-impl/src/main/groovy/org/gradle/api/internal/artifacts/ivyservice/resolveengine/ModuleConflictResolver.java b/subprojects/core-impl/src/main/groovy/org/gradle/api/internal/artifacts/ivyservice/resolveengine/ModuleConflictResolver.java
index 4336b9e..7450d1f 100644
--- a/subprojects/core-impl/src/main/groovy/org/gradle/api/internal/artifacts/ivyservice/resolveengine/ModuleConflictResolver.java
+++ b/subprojects/core-impl/src/main/groovy/org/gradle/api/internal/artifacts/ivyservice/resolveengine/ModuleConflictResolver.java
@@ -18,5 +18,5 @@ package org.gradle.api.internal.artifacts.ivyservice.resolveengine;
 import java.util.Collection;
 
 interface ModuleConflictResolver {
-    ModuleRevisionResolveState select(Collection<? extends ModuleRevisionResolveState> candidates, ModuleRevisionResolveState root);
+    ModuleRevisionResolveState select(Collection<? extends ModuleRevisionResolveState> candidates);
 }
diff --git a/subprojects/core-impl/src/main/groovy/org/gradle/api/internal/artifacts/ivyservice/resolveengine/ModuleRevisionResolveState.java b/subprojects/core-impl/src/main/groovy/org/gradle/api/internal/artifacts/ivyservice/resolveengine/ModuleRevisionResolveState.java
index 6decc2c..174353d 100644
--- a/subprojects/core-impl/src/main/groovy/org/gradle/api/internal/artifacts/ivyservice/resolveengine/ModuleRevisionResolveState.java
+++ b/subprojects/core-impl/src/main/groovy/org/gradle/api/internal/artifacts/ivyservice/resolveengine/ModuleRevisionResolveState.java
@@ -16,12 +16,11 @@
 package org.gradle.api.internal.artifacts.ivyservice.resolveengine;
 
 import org.gradle.api.artifacts.result.ModuleVersionSelectionReason;
+import org.gradle.api.internal.artifacts.ivyservice.ivyresolve.Versioned;
 
-interface ModuleRevisionResolveState {
+interface ModuleRevisionResolveState extends Versioned {
     String getId();
 
-    String getRevision();
-
     ModuleVersionSelectionReason getSelectionReason();
 
     void setSelectionReason(ModuleVersionSelectionReason moduleVersionSelectionReason);
diff --git a/subprojects/core-impl/src/main/groovy/org/gradle/api/internal/artifacts/ivyservice/resolveengine/StrictConflictResolver.java b/subprojects/core-impl/src/main/groovy/org/gradle/api/internal/artifacts/ivyservice/resolveengine/StrictConflictResolver.java
index 86f6daf..a78b3bb 100644
--- a/subprojects/core-impl/src/main/groovy/org/gradle/api/internal/artifacts/ivyservice/resolveengine/StrictConflictResolver.java
+++ b/subprojects/core-impl/src/main/groovy/org/gradle/api/internal/artifacts/ivyservice/resolveengine/StrictConflictResolver.java
@@ -19,7 +19,7 @@ import java.util.Collection;
 import java.util.Formatter;
 
 class StrictConflictResolver implements ModuleConflictResolver {
-    public ModuleRevisionResolveState select(Collection<? extends ModuleRevisionResolveState> candidates, ModuleRevisionResolveState root) {
+    public ModuleRevisionResolveState select(Collection<? extends ModuleRevisionResolveState> candidates) {
         Formatter formatter = new Formatter();
         formatter.format("A conflict was found between the following modules:");
         for (ModuleRevisionResolveState candidate : candidates) {
diff --git a/subprojects/core-impl/src/main/groovy/org/gradle/api/internal/artifacts/ivyservice/resolveengine/VersionSelectionReasonResolver.java b/subprojects/core-impl/src/main/groovy/org/gradle/api/internal/artifacts/ivyservice/resolveengine/VersionSelectionReasonResolver.java
index cb0d3c2..b4c184c 100644
--- a/subprojects/core-impl/src/main/groovy/org/gradle/api/internal/artifacts/ivyservice/resolveengine/VersionSelectionReasonResolver.java
+++ b/subprojects/core-impl/src/main/groovy/org/gradle/api/internal/artifacts/ivyservice/resolveengine/VersionSelectionReasonResolver.java
@@ -20,9 +20,6 @@ import org.gradle.api.internal.artifacts.ivyservice.resolveengine.result.Version
 
 import java.util.Collection;
 
-/**
-* by Szczepan Faber, created at: 1/29/13
-*/
 public class VersionSelectionReasonResolver implements ModuleConflictResolver {
 
     private final ModuleConflictResolver delegate;
@@ -31,8 +28,8 @@ public class VersionSelectionReasonResolver implements ModuleConflictResolver {
         this.delegate = delegate;
     }
 
-    public ModuleRevisionResolveState select(Collection<? extends ModuleRevisionResolveState> candidates, ModuleRevisionResolveState root) {
-        ModuleRevisionResolveState selected = delegate.select(candidates, root);
+    public ModuleRevisionResolveState select(Collection<? extends ModuleRevisionResolveState> candidates) {
+        ModuleRevisionResolveState selected = delegate.select(candidates);
         selected.setSelectionReason(VersionSelectionReasons.withConflictResolution(selected.getSelectionReason()));
         return selected;
     }
diff --git a/subprojects/core-impl/src/main/groovy/org/gradle/api/internal/artifacts/ivyservice/resolveengine/oldresult/DefaultResolvedConfigurationBuilder.java b/subprojects/core-impl/src/main/groovy/org/gradle/api/internal/artifacts/ivyservice/resolveengine/oldresult/DefaultResolvedConfigurationBuilder.java
new file mode 100644
index 0000000..2b7cfc6
--- /dev/null
+++ b/subprojects/core-impl/src/main/groovy/org/gradle/api/internal/artifacts/ivyservice/resolveengine/oldresult/DefaultResolvedConfigurationBuilder.java
@@ -0,0 +1,134 @@
+/*
+ * Copyright 2013 the original author or authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY 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.resolveengine.oldresult;
+
+import org.apache.ivy.core.module.descriptor.Artifact;
+import org.gradle.api.artifacts.ModuleDependency;
+import org.gradle.api.artifacts.ResolvedArtifact;
+import org.gradle.api.artifacts.ResolvedDependency;
+import org.gradle.api.artifacts.UnresolvedDependency;
+import org.gradle.api.internal.artifacts.DefaultResolvedArtifact;
+import org.gradle.api.internal.artifacts.ResolvedConfigurationIdentifier;
+import org.gradle.api.internal.artifacts.ivyservice.ArtifactResolver;
+import org.gradle.api.internal.artifacts.ivyservice.ResolvedArtifactFactory;
+import org.gradle.api.internal.artifacts.ivyservice.dynamicversions.DefaultResolvedModuleVersion;
+import org.gradle.internal.Factory;
+import org.gradle.internal.id.IdGenerator;
+import org.gradle.internal.id.LongIdGenerator;
+
+import java.io.File;
+import java.util.*;
+
+public class DefaultResolvedConfigurationBuilder implements
+        ResolvedConfigurationBuilder, ResolvedConfigurationResults, ResolvedContentsMapping {
+
+    private final Map<Long, ResolvedArtifact> artifacts = new LinkedHashMap<Long, ResolvedArtifact>();
+    private final Set<UnresolvedDependency> unresolvedDependencies = new LinkedHashSet<UnresolvedDependency>();
+    private final IdGenerator<Long> idGenerator = new LongIdGenerator();
+    private final Map<ResolvedConfigurationIdentifier, ModuleDependency> modulesMap = new HashMap<ResolvedConfigurationIdentifier, ModuleDependency>();
+
+    private ResolvedArtifactFactory resolvedArtifactFactory;
+
+    private final TransientResultsStore store;
+
+    public DefaultResolvedConfigurationBuilder(ResolvedArtifactFactory resolvedArtifactFactory, TransientResultsStore resultsStore) {
+        this.resolvedArtifactFactory = resolvedArtifactFactory;
+        this.store = resultsStore;
+    }
+
+    public void addUnresolvedDependency(UnresolvedDependency unresolvedDependency) {
+        unresolvedDependencies.add(unresolvedDependency);
+    }
+
+    public void addFirstLevelDependency(ModuleDependency moduleDependency, ResolvedConfigurationIdentifier dependency) {
+        store.firstLevelDependency(dependency);
+        //we don't serialise the module dependencies at this stage so we need to keep track
+        //of the mapping module dependency <-> resolved dependency
+        modulesMap.put(dependency, moduleDependency);
+    }
+
+    public void done(ResolvedConfigurationIdentifier root) {
+        store.done(root);
+    }
+
+    public void addChild(ResolvedConfigurationIdentifier parent, ResolvedConfigurationIdentifier child) {
+        store.parentChildMapping(parent, child);
+    }
+
+    public void addParentSpecificArtifacts(ResolvedConfigurationIdentifier child, ResolvedConfigurationIdentifier parent, Set<ResolvedArtifact> artifacts) {
+        for (ResolvedArtifact a : artifacts) {
+            store.parentSpecificArtifact(child, parent, ((DefaultResolvedArtifact)a).getId());
+        }
+    }
+
+    public void newResolvedDependency(ResolvedConfigurationIdentifier id) {
+        store.resolvedDependency(id);
+    }
+
+    public ResolvedArtifact newArtifact(final ResolvedConfigurationIdentifier owner, Artifact artifact, ArtifactResolver artifactResolver) {
+        Factory<File> artifactSource = resolvedArtifactFactory.artifactSource(artifact, artifactResolver);
+        Factory<ResolvedDependency> dependencySource = new ResolvedDependencyFactory(owner, store, this);
+        long id = idGenerator.generateId();
+        ResolvedArtifact newArtifact = new DefaultResolvedArtifact(new DefaultResolvedModuleVersion(owner.getId()), dependencySource, artifact, artifactSource, id);
+        artifacts.put(id, newArtifact);
+        return newArtifact;
+    }
+
+    public boolean hasError() {
+        return !unresolvedDependencies.isEmpty();
+    }
+
+    public TransientConfigurationResults more() {
+        return store.load(this);
+    }
+
+    public Set<ResolvedArtifact> getArtifacts() {
+        return new LinkedHashSet<ResolvedArtifact>(artifacts.values());
+    }
+
+    public ResolvedArtifact getArtifact(long artifactId) {
+        ResolvedArtifact a = artifacts.get(artifactId);
+        assert a != null : "Unable to find artifact for id: " + artifactId;
+        return a;
+    }
+
+    public ModuleDependency getModuleDependency(ResolvedConfigurationIdentifier id) {
+        ModuleDependency m = modulesMap.get(id);
+        assert m != null : "Unable to find module dependency for id: " + id;
+        return m;
+    }
+
+    public Set<UnresolvedDependency> getUnresolvedDependencies() {
+        return unresolvedDependencies;
+    }
+
+    private static class ResolvedDependencyFactory implements Factory<ResolvedDependency> {
+        private final ResolvedConfigurationIdentifier owner;
+        private TransientResultsStore store;
+        private ResolvedContentsMapping mapping;
+
+        public ResolvedDependencyFactory(ResolvedConfigurationIdentifier owner, TransientResultsStore store, ResolvedContentsMapping mapping) {
+            this.owner = owner;
+            this.store = store;
+            this.mapping = mapping;
+        }
+
+        public ResolvedDependency create() {
+            return store.load(mapping).getResolvedDependency(owner);
+        }
+    }
+}
diff --git a/subprojects/core-impl/src/main/groovy/org/gradle/api/internal/artifacts/ivyservice/resolveengine/oldresult/DefaultTransientConfigurationResults.java b/subprojects/core-impl/src/main/groovy/org/gradle/api/internal/artifacts/ivyservice/resolveengine/oldresult/DefaultTransientConfigurationResults.java
new file mode 100644
index 0000000..0c8adad
--- /dev/null
+++ b/subprojects/core-impl/src/main/groovy/org/gradle/api/internal/artifacts/ivyservice/resolveengine/oldresult/DefaultTransientConfigurationResults.java
@@ -0,0 +1,45 @@
+/*
+ * Copyright 2013 the original author or authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY 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.resolveengine.oldresult;
+
+import org.gradle.api.artifacts.ModuleDependency;
+import org.gradle.api.artifacts.ResolvedDependency;
+import org.gradle.api.internal.artifacts.DefaultResolvedDependency;
+import org.gradle.api.internal.artifacts.ResolvedConfigurationIdentifier;
+
+import java.util.HashMap;
+import java.util.LinkedHashMap;
+import java.util.Map;
+
+public class DefaultTransientConfigurationResults implements TransientConfigurationResults {
+
+    final Map<ModuleDependency, ResolvedDependency> firstLevelDependencies = new LinkedHashMap<ModuleDependency, ResolvedDependency>();
+    ResolvedDependency root;
+    final Map<ResolvedConfigurationIdentifier, DefaultResolvedDependency> allDependencies = new HashMap<ResolvedConfigurationIdentifier, DefaultResolvedDependency>();
+
+    public Map<ModuleDependency, ResolvedDependency> getFirstLevelDependencies() {
+        return firstLevelDependencies;
+    }
+
+    public ResolvedDependency getRoot() {
+        return root;
+    }
+
+    public ResolvedDependency getResolvedDependency(ResolvedConfigurationIdentifier id) {
+        return allDependencies.get(id);
+    }
+}
diff --git a/subprojects/core-impl/src/main/groovy/org/gradle/api/internal/artifacts/ivyservice/resolveengine/oldresult/ResolvedConfigurationBuilder.java b/subprojects/core-impl/src/main/groovy/org/gradle/api/internal/artifacts/ivyservice/resolveengine/oldresult/ResolvedConfigurationBuilder.java
new file mode 100644
index 0000000..55739e9
--- /dev/null
+++ b/subprojects/core-impl/src/main/groovy/org/gradle/api/internal/artifacts/ivyservice/resolveengine/oldresult/ResolvedConfigurationBuilder.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.artifacts.ivyservice.resolveengine.oldresult;
+
+import org.apache.ivy.core.module.descriptor.Artifact;
+import org.gradle.api.artifacts.ModuleDependency;
+import org.gradle.api.artifacts.ResolvedArtifact;
+import org.gradle.api.artifacts.UnresolvedDependency;
+import org.gradle.api.internal.artifacts.ResolvedConfigurationIdentifier;
+import org.gradle.api.internal.artifacts.ivyservice.ArtifactResolver;
+
+import java.util.Set;
+
+//listens to result events tailored for old resolved dependency graph
+public interface ResolvedConfigurationBuilder {
+
+    void addFirstLevelDependency(ModuleDependency moduleDependency, ResolvedConfigurationIdentifier dependency);
+
+    void addUnresolvedDependency(UnresolvedDependency unresolvedDependency);
+
+    void addChild(ResolvedConfigurationIdentifier parent, ResolvedConfigurationIdentifier child);
+
+    void done(ResolvedConfigurationIdentifier root);
+
+    void addParentSpecificArtifacts(ResolvedConfigurationIdentifier child, ResolvedConfigurationIdentifier parent, Set<ResolvedArtifact> artifacts);
+
+    void newResolvedDependency(ResolvedConfigurationIdentifier id);
+
+    ResolvedArtifact newArtifact(ResolvedConfigurationIdentifier owner, Artifact artifact, ArtifactResolver artifactResolver);
+}
diff --git a/subprojects/core-impl/src/main/groovy/org/gradle/api/internal/artifacts/ivyservice/resolveengine/oldresult/ResolvedConfigurationResults.java b/subprojects/core-impl/src/main/groovy/org/gradle/api/internal/artifacts/ivyservice/resolveengine/oldresult/ResolvedConfigurationResults.java
new file mode 100644
index 0000000..34c0cb0
--- /dev/null
+++ b/subprojects/core-impl/src/main/groovy/org/gradle/api/internal/artifacts/ivyservice/resolveengine/oldresult/ResolvedConfigurationResults.java
@@ -0,0 +1,32 @@
+/*
+ * Copyright 2013 the original author or authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY 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.resolveengine.oldresult;
+
+import org.gradle.api.artifacts.ResolvedArtifact;
+import org.gradle.api.artifacts.UnresolvedDependency;
+
+import java.util.Set;
+
+public interface ResolvedConfigurationResults {
+    boolean hasError();
+
+    Set<UnresolvedDependency> getUnresolvedDependencies();
+
+    Set<ResolvedArtifact> getArtifacts();
+
+    TransientConfigurationResults more();
+}
diff --git a/subprojects/core-impl/src/main/groovy/org/gradle/api/internal/artifacts/ivyservice/resolveengine/oldresult/ResolvedContentsMapping.java b/subprojects/core-impl/src/main/groovy/org/gradle/api/internal/artifacts/ivyservice/resolveengine/oldresult/ResolvedContentsMapping.java
new file mode 100644
index 0000000..7fb9266
--- /dev/null
+++ b/subprojects/core-impl/src/main/groovy/org/gradle/api/internal/artifacts/ivyservice/resolveengine/oldresult/ResolvedContentsMapping.java
@@ -0,0 +1,28 @@
+/*
+ * Copyright 2013 the original author or authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY 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.resolveengine.oldresult;
+
+import org.gradle.api.artifacts.ModuleDependency;
+import org.gradle.api.artifacts.ResolvedArtifact;
+import org.gradle.api.internal.artifacts.ResolvedConfigurationIdentifier;
+
+public interface ResolvedContentsMapping {
+
+    ResolvedArtifact getArtifact(long id);
+
+    ModuleDependency getModuleDependency(ResolvedConfigurationIdentifier id);
+}
diff --git a/subprojects/core-impl/src/main/groovy/org/gradle/api/internal/artifacts/ivyservice/resolveengine/oldresult/TransientConfigurationResults.java b/subprojects/core-impl/src/main/groovy/org/gradle/api/internal/artifacts/ivyservice/resolveengine/oldresult/TransientConfigurationResults.java
new file mode 100644
index 0000000..634f969
--- /dev/null
+++ b/subprojects/core-impl/src/main/groovy/org/gradle/api/internal/artifacts/ivyservice/resolveengine/oldresult/TransientConfigurationResults.java
@@ -0,0 +1,32 @@
+/*
+ * Copyright 2013 the original author or authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY 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.resolveengine.oldresult;
+
+import org.gradle.api.artifacts.ModuleDependency;
+import org.gradle.api.artifacts.ResolvedDependency;
+import org.gradle.api.internal.artifacts.ResolvedConfigurationIdentifier;
+
+import java.util.Map;
+
+public interface TransientConfigurationResults {
+
+    Map<ModuleDependency, ResolvedDependency> getFirstLevelDependencies();
+
+    ResolvedDependency getRoot();
+
+    ResolvedDependency getResolvedDependency(ResolvedConfigurationIdentifier id);
+}
diff --git a/subprojects/core-impl/src/main/groovy/org/gradle/api/internal/artifacts/ivyservice/resolveengine/oldresult/TransientResultsStore.java b/subprojects/core-impl/src/main/groovy/org/gradle/api/internal/artifacts/ivyservice/resolveengine/oldresult/TransientResultsStore.java
new file mode 100644
index 0000000..b267800
--- /dev/null
+++ b/subprojects/core-impl/src/main/groovy/org/gradle/api/internal/artifacts/ivyservice/resolveengine/oldresult/TransientResultsStore.java
@@ -0,0 +1,185 @@
+/*
+ * Copyright 2013 the original author or authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY 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.resolveengine.oldresult;
+
+import org.gradle.api.internal.artifacts.DefaultResolvedDependency;
+import org.gradle.api.internal.artifacts.ResolvedConfigurationIdentifier;
+import org.gradle.api.internal.artifacts.ResolvedConfigurationIdentifierSerializer;
+import org.gradle.api.internal.artifacts.ivyservice.resolveengine.store.EncodedWriteAction;
+import org.gradle.api.internal.cache.BinaryStore;
+import org.gradle.api.internal.cache.Store;
+import org.gradle.api.logging.Logger;
+import org.gradle.api.logging.Logging;
+import org.gradle.internal.Factory;
+import org.gradle.messaging.serialize.FlushableEncoder;
+import org.gradle.messaging.serialize.InputStreamBackedDecoder;
+import org.gradle.util.Clock;
+
+import java.io.DataInputStream;
+import java.io.DataOutputStream;
+import java.io.IOException;
+
+import static com.google.common.collect.Sets.newHashSet;
+
+//TODO SF unit coverage
+public class TransientResultsStore {
+
+    private final static Logger LOG = Logging.getLogger(TransientResultsStore.class);
+
+    private static final byte NEW_DEP = 1;
+    private static final byte ROOT = 2;
+    private static final byte FIRST_LVL = 3;
+    private static final byte PARENT_CHILD = 4;
+    private static final byte PARENT_ARTIFACT = 5;
+
+    private final Object lock = new Object();
+
+    private BinaryStore binaryStore;
+    private Store<TransientConfigurationResults> cache;
+    private final ResolvedConfigurationIdentifierSerializer resolvedConfigurationIdentifierSerializer = new ResolvedConfigurationIdentifierSerializer();
+    private BinaryStore.BinaryData binaryData;
+
+    public TransientResultsStore(BinaryStore binaryStore, Store<TransientConfigurationResults> cache) {
+        this.binaryStore = binaryStore;
+        this.cache = cache;
+    }
+
+    private void writeId(final byte type, final ResolvedConfigurationIdentifier... ids) {
+        binaryStore.write(new EncodedWriteAction() {
+            public void write(FlushableEncoder encoder) throws IOException {
+                encoder.writeByte(type);
+                for (ResolvedConfigurationIdentifier id : ids) {
+                    resolvedConfigurationIdentifierSerializer.write(encoder, id);
+                }
+            }
+        });
+    }
+
+    public void resolvedDependency(ResolvedConfigurationIdentifier id) {
+        writeId(NEW_DEP, id);
+    }
+
+    public void done(ResolvedConfigurationIdentifier id) {
+        writeId(ROOT, id);
+        LOG.debug("Flushing resolved configuration data in {}. Wrote root {}.", binaryStore, id);
+        binaryData = binaryStore.done();
+    }
+
+    public void firstLevelDependency(ResolvedConfigurationIdentifier id) {
+        writeId(FIRST_LVL, id);
+    }
+
+    public void parentChildMapping(ResolvedConfigurationIdentifier parent, ResolvedConfigurationIdentifier child) {
+        writeId(PARENT_CHILD, parent, child);
+    }
+
+    public void parentSpecificArtifact(ResolvedConfigurationIdentifier child, ResolvedConfigurationIdentifier parent, final long artifactId) {
+        writeId(PARENT_ARTIFACT, child, parent);
+        binaryStore.write(new BinaryStore.WriteAction() {
+            public void write(DataOutputStream output) throws IOException {
+                output.writeLong(artifactId);
+            }
+        });
+    }
+
+    public TransientConfigurationResults load(final ResolvedContentsMapping mapping) {
+        synchronized (lock) {
+            return cache.load(new Factory<TransientConfigurationResults>() {
+                public TransientConfigurationResults create() {
+                    try {
+                        return binaryData.read(new BinaryStore.ReadAction<TransientConfigurationResults>() {
+                            public TransientConfigurationResults read(DataInputStream input) throws IOException {
+                                return deserialize(input, mapping);
+                            }
+                        });
+                    } finally {
+                        binaryData.done();
+                    }
+                }
+            });
+        }
+    }
+
+    private TransientConfigurationResults deserialize(DataInputStream input, ResolvedContentsMapping mapping) {
+        Clock clock = new Clock();
+        DefaultTransientConfigurationResults results = new DefaultTransientConfigurationResults();
+        int valuesRead = 0;
+        byte type = -1;
+        InputStreamBackedDecoder decoder = new InputStreamBackedDecoder(input);
+        try {
+            while (true) {
+                type = decoder.readByte();
+                ResolvedConfigurationIdentifier id;
+                valuesRead++;
+                switch (type) {
+                    case NEW_DEP:
+                        id = resolvedConfigurationIdentifierSerializer.read(decoder);
+                        results.allDependencies.put(id, new DefaultResolvedDependency(id.getId(), id.getConfiguration()));
+                        break;
+                    case ROOT:
+                        id = resolvedConfigurationIdentifierSerializer.read(decoder);
+                        results.root = results.allDependencies.get(id);
+                        if (results.root == null) {
+                            throw new IllegalStateException(String.format("Unexpected root id %s. Seen ids: %s", id, results.allDependencies.keySet()));
+                        }
+                        //root should be the last
+                        LOG.debug("Loaded resolved configuration results ({}) from {}", clock.getTime(), binaryStore);
+                        return results;
+                    case FIRST_LVL:
+                        id = resolvedConfigurationIdentifierSerializer.read(decoder);
+                        DefaultResolvedDependency dependency = results.allDependencies.get(id);
+                        if (dependency == null) {
+                            throw new IllegalStateException(String.format("Unexpected first level id %s. Seen ids: %s", id, results.allDependencies.keySet()));
+                        }
+                        results.firstLevelDependencies.put(mapping.getModuleDependency(id), dependency);
+                        break;
+                    case PARENT_CHILD:
+                        ResolvedConfigurationIdentifier parentId = resolvedConfigurationIdentifierSerializer.read(decoder);
+                        ResolvedConfigurationIdentifier childId = resolvedConfigurationIdentifierSerializer.read(decoder);
+                        DefaultResolvedDependency parent = results.allDependencies.get(parentId);
+                        DefaultResolvedDependency child = results.allDependencies.get(childId);
+                        if (parent == null) {
+                            throw new IllegalStateException(String.format("Unexpected parent dependency id %s. Seen ids: %s", parentId, results.allDependencies.keySet()));
+                        }
+                        if (child == null) {
+                            throw new IllegalStateException(String.format("Unexpected child dependency id %s. Seen ids: %s", childId, results.allDependencies.keySet()));
+                        }
+                        parent.addChild(child);
+                        break;
+                    case PARENT_ARTIFACT:
+                        ResolvedConfigurationIdentifier artifactParentId = resolvedConfigurationIdentifierSerializer.read(decoder);
+                        ResolvedConfigurationIdentifier artifactChildId = resolvedConfigurationIdentifierSerializer.read(decoder);
+                        DefaultResolvedDependency artifactParent = results.allDependencies.get(artifactParentId);
+                        DefaultResolvedDependency artifactChild = results.allDependencies.get(artifactChildId);
+                        if (artifactParent == null) {
+                            throw new IllegalStateException(String.format("Unexpected parent dependency id %s. Seen ids: %s", artifactParentId, results.allDependencies.keySet()));
+                        }
+                        if (artifactChild == null) {
+                            throw new IllegalStateException(String.format("Unexpected child dependency id %s. Seen ids: %s", artifactChildId, results.allDependencies.keySet()));
+                        }
+                        artifactParent.addParentSpecificArtifacts(artifactChild, newHashSet(mapping.getArtifact(decoder.readLong())));
+                        break;
+                    default:
+                        throw new IOException("Unknown value type read from stream: " + type);
+                }
+            }
+        } catch (IOException e) {
+            throw new RuntimeException("Problems loading the resolved configuration. Read " + valuesRead + " values, last was: " + type, e);
+        }
+    }
+}
diff --git a/subprojects/core-impl/src/main/groovy/org/gradle/api/internal/artifacts/ivyservice/resolveengine/result/CachingDependencyResultFactory.java b/subprojects/core-impl/src/main/groovy/org/gradle/api/internal/artifacts/ivyservice/resolveengine/result/CachingDependencyResultFactory.java
index 95d6eff..7340657 100644
--- a/subprojects/core-impl/src/main/groovy/org/gradle/api/internal/artifacts/ivyservice/resolveengine/result/CachingDependencyResultFactory.java
+++ b/subprojects/core-impl/src/main/groovy/org/gradle/api/internal/artifacts/ivyservice/resolveengine/result/CachingDependencyResultFactory.java
@@ -23,6 +23,7 @@ import org.gradle.api.artifacts.result.ResolvedModuleVersionResult;
 import org.gradle.api.artifacts.result.UnresolvedDependencyResult;
 import org.gradle.api.internal.artifacts.ivyservice.ModuleVersionResolveException;
 import org.gradle.api.internal.artifacts.result.DefaultResolvedDependencyResult;
+import org.gradle.api.internal.artifacts.result.DefaultResolvedModuleVersionResult;
 import org.gradle.api.internal.artifacts.result.DefaultUnresolvedDependencyResult;
 
 import java.util.HashMap;
@@ -31,9 +32,6 @@ import java.util.Map;
 
 import static java.util.Arrays.asList;
 
-/**
- * by Szczepan Faber, created at: 10/1/12
- */
 public class CachingDependencyResultFactory {
 
     private final Map<List, DefaultUnresolvedDependencyResult> unresolvedDependencies = new HashMap<List, DefaultUnresolvedDependencyResult>();
@@ -48,7 +46,7 @@ public class CachingDependencyResultFactory {
         return unresolvedDependencies.get(key);
     }
 
-    public ResolvedDependencyResult createResolvedDependency(ModuleVersionSelector requested, ResolvedModuleVersionResult from, ResolvedModuleVersionResult selected) {
+    public ResolvedDependencyResult createResolvedDependency(ModuleVersionSelector requested, ResolvedModuleVersionResult from, DefaultResolvedModuleVersionResult selected) {
         List<Object> key = asList(requested, from, selected);
         if (!resolvedDependencies.containsKey(key)) {
             resolvedDependencies.put(key, new DefaultResolvedDependencyResult(requested, selected, from));
diff --git a/subprojects/core-impl/src/main/groovy/org/gradle/api/internal/artifacts/ivyservice/resolveengine/result/DefaultInternalDependencyResult.java b/subprojects/core-impl/src/main/groovy/org/gradle/api/internal/artifacts/ivyservice/resolveengine/result/DefaultInternalDependencyResult.java
new file mode 100644
index 0000000..5bac938
--- /dev/null
+++ b/subprojects/core-impl/src/main/groovy/org/gradle/api/internal/artifacts/ivyservice/resolveengine/result/DefaultInternalDependencyResult.java
@@ -0,0 +1,59 @@
+/*
+ * Copyright 2013 the original author or authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY 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.resolveengine.result;
+
+import org.gradle.api.artifacts.ModuleVersionSelector;
+import org.gradle.api.artifacts.result.ModuleVersionSelectionReason;
+import org.gradle.api.internal.artifacts.ivyservice.ModuleVersionResolveException;
+
+public class DefaultInternalDependencyResult implements InternalDependencyResult {
+
+    private final ModuleVersionSelector requested;
+    private final ModuleVersionSelection selected;
+    private final ModuleVersionSelectionReason reason;
+    private ModuleVersionResolveException failure;
+
+    public DefaultInternalDependencyResult(ModuleVersionSelector requested,
+                                           ModuleVersionSelection selected,
+                                           ModuleVersionSelectionReason reason,
+                                           ModuleVersionResolveException failure) {
+        assert requested != null;
+        assert reason != null;
+        assert failure != null || selected != null;
+
+        this.requested = requested;
+        this.reason = reason;
+        this.selected = selected;
+        this.failure = failure;
+    }
+
+    public ModuleVersionSelector getRequested() {
+        return requested;
+    }
+
+    public ModuleVersionSelection getSelected() {
+        return selected;
+    }
+
+    public ModuleVersionSelectionReason getReason() {
+        return reason;
+    }
+
+    public ModuleVersionResolveException getFailure() {
+        return failure;
+    }
+}
diff --git a/subprojects/core-impl/src/main/groovy/org/gradle/api/internal/artifacts/ivyservice/resolveengine/result/DefaultModuleVersionSelection.java b/subprojects/core-impl/src/main/groovy/org/gradle/api/internal/artifacts/ivyservice/resolveengine/result/DefaultModuleVersionSelection.java
new file mode 100644
index 0000000..600d1c6
--- /dev/null
+++ b/subprojects/core-impl/src/main/groovy/org/gradle/api/internal/artifacts/ivyservice/resolveengine/result/DefaultModuleVersionSelection.java
@@ -0,0 +1,38 @@
+/*
+ * Copyright 2013 the original author or authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY 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.resolveengine.result;
+
+import org.gradle.api.artifacts.ModuleVersionIdentifier;
+import org.gradle.api.artifacts.result.ModuleVersionSelectionReason;
+
+class DefaultModuleVersionSelection implements ModuleVersionSelection {
+    private ModuleVersionIdentifier id;
+    private ModuleVersionSelectionReason reason;
+
+    public DefaultModuleVersionSelection(ModuleVersionIdentifier id, ModuleVersionSelectionReason reason) {
+        this.id = id;
+        this.reason = reason;
+    }
+
+    public ModuleVersionIdentifier getSelectedId() {
+        return id;
+    }
+
+    public ModuleVersionSelectionReason getSelectionReason() {
+        return reason;
+    }
+}
diff --git a/subprojects/core-impl/src/main/groovy/org/gradle/api/internal/artifacts/ivyservice/resolveengine/result/InternalDependencyResult.java b/subprojects/core-impl/src/main/groovy/org/gradle/api/internal/artifacts/ivyservice/resolveengine/result/InternalDependencyResult.java
index 13250d2..4074c7e 100644
--- a/subprojects/core-impl/src/main/groovy/org/gradle/api/internal/artifacts/ivyservice/resolveengine/result/InternalDependencyResult.java
+++ b/subprojects/core-impl/src/main/groovy/org/gradle/api/internal/artifacts/ivyservice/resolveengine/result/InternalDependencyResult.java
@@ -21,9 +21,6 @@ import org.gradle.api.artifacts.ModuleVersionSelector;
 import org.gradle.api.artifacts.result.ModuleVersionSelectionReason;
 import org.gradle.api.internal.artifacts.ivyservice.ModuleVersionResolveException;
 
-/**
- * by Szczepan Faber, created at: 8/24/12
- */
 public interface InternalDependencyResult {
 
     ModuleVersionSelector getRequested();
diff --git a/subprojects/core-impl/src/main/groovy/org/gradle/api/internal/artifacts/ivyservice/resolveengine/result/InternalDependencyResultSerializer.java b/subprojects/core-impl/src/main/groovy/org/gradle/api/internal/artifacts/ivyservice/resolveengine/result/InternalDependencyResultSerializer.java
new file mode 100644
index 0000000..0636a21
--- /dev/null
+++ b/subprojects/core-impl/src/main/groovy/org/gradle/api/internal/artifacts/ivyservice/resolveengine/result/InternalDependencyResultSerializer.java
@@ -0,0 +1,61 @@
+/*
+ * Copyright 2013 the original author or authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY 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.resolveengine.result;
+
+import org.gradle.api.artifacts.ModuleVersionSelector;
+import org.gradle.api.artifacts.result.ModuleVersionSelectionReason;
+import org.gradle.api.internal.artifacts.ModuleVersionSelectorSerializer;
+import org.gradle.api.internal.artifacts.ivyservice.ModuleVersionResolveException;
+import org.gradle.messaging.serialize.Decoder;
+import org.gradle.messaging.serialize.Encoder;
+
+import java.io.IOException;
+import java.util.Map;
+
+public class InternalDependencyResultSerializer {
+    private final static byte SUCCESSFUL = 0;
+    private final static byte FAILED = 1;
+    private final ModuleVersionSelectorSerializer moduleVersionSelectorSerializer = new ModuleVersionSelectorSerializer();
+    private final ModuleVersionSelectionReasonSerializer moduleVersionSelectionReasonSerializer = new ModuleVersionSelectionReasonSerializer();
+    private final ModuleVersionSelectionSerializer moduleVersionSelectionSerializer = new ModuleVersionSelectionSerializer();
+
+    public InternalDependencyResult read(Decoder decoder, Map<ModuleVersionSelector, ModuleVersionResolveException> failures) throws IOException {
+        ModuleVersionSelector requested = moduleVersionSelectorSerializer.read(decoder);
+        ModuleVersionSelectionReason reason = moduleVersionSelectionReasonSerializer.read(decoder);
+        byte resultByte = decoder.readByte();
+        if (resultByte == SUCCESSFUL) {
+            ModuleVersionSelection selected = moduleVersionSelectionSerializer.read(decoder);
+            return new DefaultInternalDependencyResult(requested, selected, reason, null);
+        } else if (resultByte == FAILED) {
+            ModuleVersionResolveException failure = failures.get(requested);
+            return new DefaultInternalDependencyResult(requested, null, reason, failure);
+        } else {
+            throw new IllegalArgumentException("Unknown result byte: " + resultByte);
+        }
+    }
+
+    public void write(Encoder encoder, InternalDependencyResult value) throws IOException {
+        moduleVersionSelectorSerializer.write(encoder, value.getRequested());
+        moduleVersionSelectionReasonSerializer.write(encoder, value.getReason());
+        if (value.getFailure() == null) {
+            encoder.writeByte(SUCCESSFUL);
+            moduleVersionSelectionSerializer.write(encoder, value.getSelected());
+        } else {
+            encoder.writeByte(FAILED);
+        }
+    }
+}
diff --git a/subprojects/core-impl/src/main/groovy/org/gradle/api/internal/artifacts/ivyservice/resolveengine/result/ModuleVersionSelection.java b/subprojects/core-impl/src/main/groovy/org/gradle/api/internal/artifacts/ivyservice/resolveengine/result/ModuleVersionSelection.java
index bf702f4..11b8849 100644
--- a/subprojects/core-impl/src/main/groovy/org/gradle/api/internal/artifacts/ivyservice/resolveengine/result/ModuleVersionSelection.java
+++ b/subprojects/core-impl/src/main/groovy/org/gradle/api/internal/artifacts/ivyservice/resolveengine/result/ModuleVersionSelection.java
@@ -19,9 +19,6 @@ package org.gradle.api.internal.artifacts.ivyservice.resolveengine.result;
 import org.gradle.api.artifacts.ModuleVersionIdentifier;
 import org.gradle.api.artifacts.result.ModuleVersionSelectionReason;
 
-/**
-* by Szczepan Faber, created at: 8/31/12
-*/
 public interface ModuleVersionSelection {
 
     ModuleVersionIdentifier getSelectedId();
diff --git a/subprojects/core-impl/src/main/groovy/org/gradle/api/internal/artifacts/ivyservice/resolveengine/result/ModuleVersionSelectionReasonSerializer.java b/subprojects/core-impl/src/main/groovy/org/gradle/api/internal/artifacts/ivyservice/resolveengine/result/ModuleVersionSelectionReasonSerializer.java
new file mode 100644
index 0000000..2595aed
--- /dev/null
+++ b/subprojects/core-impl/src/main/groovy/org/gradle/api/internal/artifacts/ivyservice/resolveengine/result/ModuleVersionSelectionReasonSerializer.java
@@ -0,0 +1,59 @@
+/*
+ * Copyright 2013 the original author or authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY 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.resolveengine.result;
+
+import com.google.common.collect.BiMap;
+import com.google.common.collect.HashBiMap;
+import org.gradle.api.artifacts.result.ModuleVersionSelectionReason;
+import org.gradle.messaging.serialize.Decoder;
+import org.gradle.messaging.serialize.Encoder;
+import org.gradle.messaging.serialize.Serializer;
+
+import java.io.IOException;
+
+import static org.gradle.api.internal.artifacts.ivyservice.resolveengine.result.VersionSelectionReasons.*;
+
+public class ModuleVersionSelectionReasonSerializer implements Serializer<ModuleVersionSelectionReason> {
+
+    private static final BiMap<Byte, ModuleVersionSelectionReason> REASONS = HashBiMap.create(6);
+
+    static {
+        REASONS.put((byte) 1, REQUESTED);
+        REASONS.put((byte) 2, ROOT);
+        REASONS.put((byte) 3, FORCED);
+        REASONS.put((byte) 4, CONFLICT_RESOLUTION);
+        REASONS.put((byte) 5, SELECTED_BY_RULE);
+        REASONS.put((byte) 6, CONFLICT_RESOLUTION_BY_RULE);
+    }
+
+    public ModuleVersionSelectionReason read(Decoder decoder) throws IOException {
+        byte id = decoder.readByte();
+        ModuleVersionSelectionReason out = REASONS.get(id);
+        if (out == null) {
+            throw new IllegalArgumentException("Unable to find selection reason with id: " + id);
+        }
+        return out;
+    }
+
+    public void write(Encoder encoder, ModuleVersionSelectionReason value) throws IOException {
+        Byte id = REASONS.inverse().get(value);
+        if (id == null) {
+            throw new IllegalArgumentException("Unknown selection reason: " + value);
+        }
+        encoder.writeByte(id);
+    }
+}
diff --git a/subprojects/core-impl/src/main/groovy/org/gradle/api/internal/artifacts/ivyservice/resolveengine/result/ModuleVersionSelectionSerializer.java b/subprojects/core-impl/src/main/groovy/org/gradle/api/internal/artifacts/ivyservice/resolveengine/result/ModuleVersionSelectionSerializer.java
new file mode 100644
index 0000000..b68b0dd
--- /dev/null
+++ b/subprojects/core-impl/src/main/groovy/org/gradle/api/internal/artifacts/ivyservice/resolveengine/result/ModuleVersionSelectionSerializer.java
@@ -0,0 +1,43 @@
+/*
+ * Copyright 2013 the original author or authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY 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.resolveengine.result;
+
+import org.gradle.api.artifacts.ModuleVersionIdentifier;
+import org.gradle.api.artifacts.result.ModuleVersionSelectionReason;
+import org.gradle.api.internal.artifacts.ModuleVersionIdentifierSerializer;
+import org.gradle.messaging.serialize.Decoder;
+import org.gradle.messaging.serialize.Encoder;
+import org.gradle.messaging.serialize.Serializer;
+
+import java.io.IOException;
+
+public class ModuleVersionSelectionSerializer implements Serializer<ModuleVersionSelection> {
+
+    private final ModuleVersionIdentifierSerializer idSerializer = new ModuleVersionIdentifierSerializer();
+    private final ModuleVersionSelectionReasonSerializer reasonSerializer = new ModuleVersionSelectionReasonSerializer();
+
+    public ModuleVersionSelection read(Decoder decoder) throws IOException {
+        ModuleVersionIdentifier id = idSerializer.read(decoder);
+        ModuleVersionSelectionReason reason = reasonSerializer.read(decoder);
+        return new DefaultModuleVersionSelection(id, reason);
+    }
+
+    public void write(Encoder encoder, ModuleVersionSelection value) throws IOException {
+        idSerializer.write(encoder, value.getSelectedId());
+        reasonSerializer.write(encoder, value.getSelectionReason());
+    }
+}
diff --git a/subprojects/core-impl/src/main/groovy/org/gradle/api/internal/artifacts/ivyservice/resolveengine/result/ResolutionResultBuilder.java b/subprojects/core-impl/src/main/groovy/org/gradle/api/internal/artifacts/ivyservice/resolveengine/result/ResolutionResultBuilder.java
index 2323616..46db100 100644
--- a/subprojects/core-impl/src/main/groovy/org/gradle/api/internal/artifacts/ivyservice/resolveengine/result/ResolutionResultBuilder.java
+++ b/subprojects/core-impl/src/main/groovy/org/gradle/api/internal/artifacts/ivyservice/resolveengine/result/ResolutionResultBuilder.java
@@ -17,19 +17,15 @@
 package org.gradle.api.internal.artifacts.ivyservice.resolveengine.result;
 
 import org.gradle.api.artifacts.ModuleVersionIdentifier;
-import org.gradle.api.artifacts.result.DependencyResult;
-import org.gradle.api.artifacts.result.ModuleVersionSelectionReason;
-import org.gradle.api.artifacts.result.ResolvedDependencyResult;
+import org.gradle.api.artifacts.result.*;
 import org.gradle.api.internal.artifacts.result.DefaultResolutionResult;
 import org.gradle.api.internal.artifacts.result.DefaultResolvedModuleVersionResult;
+import org.gradle.internal.Factory;
 
 import java.util.Collection;
 import java.util.LinkedHashMap;
 import java.util.Map;
 
-/**
- * by Szczepan Faber, created at: 7/26/12
- */
 public class ResolutionResultBuilder implements ResolvedConfigurationListener {
 
     private DefaultResolvedModuleVersionResult rootModule;
@@ -44,8 +40,8 @@ public class ResolutionResultBuilder implements ResolvedConfigurationListener {
         return this;
     }
 
-    public DefaultResolutionResult getResult() {
-        return new DefaultResolutionResult(rootModule);
+    public ResolutionResult complete() {
+        return new DefaultResolutionResult(new RootFactory(rootModule));
     }
 
     public void resolvedModuleVersion(ModuleVersionSelection moduleVersion) {
@@ -73,4 +69,16 @@ public class ResolutionResultBuilder implements ResolvedConfigurationListener {
         }
         return modules.get(id);
     }
+
+    private static class RootFactory implements Factory<ResolvedModuleVersionResult> {
+        private DefaultResolvedModuleVersionResult rootModule;
+
+        public RootFactory(DefaultResolvedModuleVersionResult rootModule) {
+            this.rootModule = rootModule;
+        }
+
+        public ResolvedModuleVersionResult create() {
+            return rootModule;
+        }
+    }
 }
\ No newline at end of file
diff --git a/subprojects/core-impl/src/main/groovy/org/gradle/api/internal/artifacts/ivyservice/resolveengine/result/ResolvedConfigurationListener.java b/subprojects/core-impl/src/main/groovy/org/gradle/api/internal/artifacts/ivyservice/resolveengine/result/ResolvedConfigurationListener.java
index 1ac0e97..ccd26d2 100644
--- a/subprojects/core-impl/src/main/groovy/org/gradle/api/internal/artifacts/ivyservice/resolveengine/result/ResolvedConfigurationListener.java
+++ b/subprojects/core-impl/src/main/groovy/org/gradle/api/internal/artifacts/ivyservice/resolveengine/result/ResolvedConfigurationListener.java
@@ -17,14 +17,14 @@
 package org.gradle.api.internal.artifacts.ivyservice.resolveengine.result;
 
 import org.gradle.api.artifacts.ModuleVersionIdentifier;
+import org.gradle.api.artifacts.result.ResolutionResult;
 
 import java.util.Collection;
 
-/**
- * by Szczepan Faber, created at: 7/26/12
- */
+//listens to result events tailored for the new dependency graph model, TODO SF rename job needed
 public interface ResolvedConfigurationListener {
     ResolvedConfigurationListener start(ModuleVersionIdentifier root);
     void resolvedModuleVersion(ModuleVersionSelection moduleVersion);
     void resolvedConfiguration(ModuleVersionIdentifier id, Collection<? extends InternalDependencyResult> dependencies);
+    ResolutionResult complete();
 }
diff --git a/subprojects/core-impl/src/main/groovy/org/gradle/api/internal/artifacts/ivyservice/resolveengine/result/StreamingResolutionResultBuilder.java b/subprojects/core-impl/src/main/groovy/org/gradle/api/internal/artifacts/ivyservice/resolveengine/result/StreamingResolutionResultBuilder.java
new file mode 100644
index 0000000..d30a709
--- /dev/null
+++ b/subprojects/core-impl/src/main/groovy/org/gradle/api/internal/artifacts/ivyservice/resolveengine/result/StreamingResolutionResultBuilder.java
@@ -0,0 +1,196 @@
+/*
+ * Copyright 2013 the original author or authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY 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.resolveengine.result;
+
+import org.gradle.api.artifacts.ModuleVersionIdentifier;
+import org.gradle.api.artifacts.ModuleVersionSelector;
+import org.gradle.api.artifacts.result.ResolutionResult;
+import org.gradle.api.artifacts.result.ResolvedModuleVersionResult;
+import org.gradle.api.internal.artifacts.ModuleVersionIdentifierSerializer;
+import org.gradle.api.internal.artifacts.ivyservice.ModuleVersionResolveException;
+import org.gradle.api.internal.artifacts.ivyservice.resolveengine.store.EncodedWriteAction;
+import org.gradle.api.internal.artifacts.result.DefaultResolutionResult;
+import org.gradle.api.internal.cache.BinaryStore;
+import org.gradle.api.internal.cache.Store;
+import org.gradle.api.logging.Logger;
+import org.gradle.api.logging.Logging;
+import org.gradle.internal.Factory;
+import org.gradle.messaging.serialize.Decoder;
+import org.gradle.messaging.serialize.FlushableEncoder;
+import org.gradle.messaging.serialize.InputStreamBackedDecoder;
+import org.gradle.util.Clock;
+
+import java.io.DataInputStream;
+import java.io.IOException;
+import java.util.*;
+
+/**
+ * by Szczepan Faber on 7/28/13
+ */
+public class StreamingResolutionResultBuilder implements ResolvedConfigurationListener {
+
+    private final static byte ROOT = 1;
+    private final static byte MODULE = 2;
+    private final static byte DEPENDENCY = 3;
+    private final static byte DONE = 4;
+
+    private final Map<ModuleVersionSelector, ModuleVersionResolveException> failures = new HashMap<ModuleVersionSelector, ModuleVersionResolveException>();
+    private final BinaryStore store;
+    private final ModuleVersionIdentifierSerializer moduleVersionIdentifierSerializer = new ModuleVersionIdentifierSerializer();
+    private final ModuleVersionSelectionSerializer moduleVersionSelectionSerializer = new ModuleVersionSelectionSerializer();
+    private Store<ResolvedModuleVersionResult> cache;
+    private final InternalDependencyResultSerializer internalDependencyResultSerializer = new InternalDependencyResultSerializer();
+
+    public StreamingResolutionResultBuilder(BinaryStore store, Store<ResolvedModuleVersionResult> cache) {
+        this.store = store;
+        this.cache = cache;
+    }
+
+    public ResolutionResult complete() {
+        store.write(new EncodedWriteAction() {
+            public void write(FlushableEncoder encoder) throws IOException {
+                encoder.writeByte(DONE);
+            }
+        });
+        BinaryStore.BinaryData data = store.done();
+        RootFactory rootSource = new RootFactory(data, failures, cache);
+        return new DefaultResolutionResult(rootSource);
+    }
+
+    public ResolvedConfigurationListener start(final ModuleVersionIdentifier root) {
+        store.write(new EncodedWriteAction() {
+            public void write(FlushableEncoder encoder) throws IOException {
+                encoder.writeByte(ROOT);
+                moduleVersionIdentifierSerializer.write(encoder, root);
+            }
+        });
+        return this;
+    }
+
+    Set<ModuleVersionIdentifier> visitedModules = new HashSet<ModuleVersionIdentifier>();
+
+    public void resolvedModuleVersion(final ModuleVersionSelection moduleVersion) {
+        if (visitedModules.add(moduleVersion.getSelectedId())) {
+            store.write(new EncodedWriteAction() {
+                public void write(FlushableEncoder encoder) throws IOException {
+                    encoder.writeByte(MODULE);
+                    moduleVersionSelectionSerializer.write(encoder, moduleVersion);
+                }
+            });
+        }
+    }
+
+    public void resolvedConfiguration(final ModuleVersionIdentifier from, final Collection<? extends InternalDependencyResult> dependencies) {
+        if (!dependencies.isEmpty()) {
+            store.write(new EncodedWriteAction() {
+                public void write(FlushableEncoder encoder) throws IOException {
+                    encoder.writeByte(DEPENDENCY);
+                    moduleVersionIdentifierSerializer.write(encoder, from);
+                    encoder.writeInt(dependencies.size());
+                    for (InternalDependencyResult dependency : dependencies) {
+                        internalDependencyResultSerializer.write(encoder, dependency);
+                        if (dependency.getFailure() != null) {
+                            //by keying the failures only be 'requested' we lose some precision
+                            //at edge case we'll lose info about a different exception if we have different failure for the same requested version
+                            failures.put(dependency.getRequested(), dependency.getFailure());
+                        }
+                    }
+                }
+            });
+        }
+    }
+
+    private static class RootFactory implements Factory<ResolvedModuleVersionResult> {
+
+        private final static Logger LOG = Logging.getLogger(RootFactory.class);
+        private final ModuleVersionSelectionSerializer moduleVersionSelectionSerializer = new ModuleVersionSelectionSerializer();
+
+        private BinaryStore.BinaryData data;
+        private final Map<ModuleVersionSelector, ModuleVersionResolveException> failures;
+        private Store<ResolvedModuleVersionResult> cache;
+        private final Object lock = new Object();
+        private final ModuleVersionIdentifierSerializer moduleVersionIdentifierSerializer = new ModuleVersionIdentifierSerializer();
+        private final InternalDependencyResultSerializer internalDependencyResultSerializer = new InternalDependencyResultSerializer();
+
+        public RootFactory(BinaryStore.BinaryData data, Map<ModuleVersionSelector, ModuleVersionResolveException> failures,
+                           Store<ResolvedModuleVersionResult> cache) {
+            this.data = data;
+            this.failures = failures;
+            this.cache = cache;
+        }
+
+        public ResolvedModuleVersionResult create() {
+            synchronized (lock) {
+                return cache.load(new Factory<ResolvedModuleVersionResult>() {
+                    public ResolvedModuleVersionResult create() {
+                        try {
+                            return data.read(new BinaryStore.ReadAction<ResolvedModuleVersionResult>() {
+                                public ResolvedModuleVersionResult read(DataInputStream input) throws IOException {
+                                    return deserialize(input);
+                                }
+                            });
+                        } finally {
+                            data.done();
+                        }
+                    }
+                });
+            }
+        }
+
+        private ResolvedModuleVersionResult deserialize(DataInputStream input) {
+            int valuesRead = 0;
+            byte type = -1;
+            Clock clock = new Clock();
+            try {
+                ResolutionResultBuilder builder = new ResolutionResultBuilder();
+                Decoder decoder = new InputStreamBackedDecoder(input);
+                while (true) {
+                    type = decoder.readByte();
+                    valuesRead++;
+                    switch (type) {
+                        case ROOT:
+                            ModuleVersionIdentifier id = moduleVersionIdentifierSerializer.read(decoder);
+                            builder.start(id);
+                            break;
+                        case MODULE:
+                            ModuleVersionSelection sel = moduleVersionSelectionSerializer.read(decoder);
+                            builder.resolvedModuleVersion(sel);
+                            break;
+                        case DEPENDENCY:
+                            id = moduleVersionIdentifierSerializer.read(decoder);
+                            int size = decoder.readInt();
+                            List<InternalDependencyResult> deps = new LinkedList<InternalDependencyResult>();
+                            for (int i = 0; i < size; i++) {
+                                deps.add(internalDependencyResultSerializer.read(decoder, failures));
+                            }
+                            builder.resolvedConfiguration(id, deps);
+                            break;
+                        case DONE:
+                            ResolvedModuleVersionResult root = builder.complete().getRoot();
+                            LOG.debug("Loaded resolution results ({}) from {}", clock.getTime(), data);
+                            return root;
+                        default:
+                            throw new IOException("Unknown value type read from stream: " + type);
+                    }
+                }
+            } catch (IOException e) {
+                throw new RuntimeException("Problems loading the resolution results (" + clock.getTime() + "). "
+                        + "Read " + valuesRead + " values, last was: " + type, e);
+            }
+        }
+    }
+}
diff --git a/subprojects/core-impl/src/main/groovy/org/gradle/api/internal/artifacts/ivyservice/resolveengine/result/VersionSelectionReasons.java b/subprojects/core-impl/src/main/groovy/org/gradle/api/internal/artifacts/ivyservice/resolveengine/result/VersionSelectionReasons.java
index 9b9e45a..39c9180 100644
--- a/subprojects/core-impl/src/main/groovy/org/gradle/api/internal/artifacts/ivyservice/resolveengine/result/VersionSelectionReasons.java
+++ b/subprojects/core-impl/src/main/groovy/org/gradle/api/internal/artifacts/ivyservice/resolveengine/result/VersionSelectionReasons.java
@@ -18,9 +18,6 @@ package org.gradle.api.internal.artifacts.ivyservice.resolveengine.result;
 
 import org.gradle.api.artifacts.result.ModuleVersionSelectionReason;
 
-/**
- * by Szczepan Faber, created at: 10/1/12
- */
 public class VersionSelectionReasons {
     public static final ModuleVersionSelectionReason REQUESTED = new DefaultModuleVersionSelectionReason(false, false, false, "requested");
     public static final ModuleVersionSelectionReason ROOT = new DefaultModuleVersionSelectionReason(false, false, false, "root");
diff --git a/subprojects/core-impl/src/main/groovy/org/gradle/api/internal/artifacts/ivyservice/resolveengine/store/CachedStoreFactory.java b/subprojects/core-impl/src/main/groovy/org/gradle/api/internal/artifacts/ivyservice/resolveengine/store/CachedStoreFactory.java
new file mode 100644
index 0000000..f618c11
--- /dev/null
+++ b/subprojects/core-impl/src/main/groovy/org/gradle/api/internal/artifacts/ivyservice/resolveengine/store/CachedStoreFactory.java
@@ -0,0 +1,101 @@
+/*
+ * Copyright 2013 the original author or authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY 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.resolveengine.store;
+
+import com.google.common.cache.Cache;
+import com.google.common.cache.CacheBuilder;
+import org.gradle.api.internal.cache.Store;
+import org.gradle.api.logging.Logger;
+import org.gradle.api.logging.Logging;
+import org.gradle.internal.Factory;
+
+import java.util.concurrent.atomic.AtomicLong;
+
+import static org.gradle.util.Clock.prettyTime;
+
+public class CachedStoreFactory<T> {
+
+    private static final Logger LOG = Logging.getLogger(CachedStoreFactory.class);
+
+    private final Cache<Object, T> cache;
+    private final Stats stats;
+    private String displayName;
+
+    public CachedStoreFactory(String displayName) {
+        this.displayName = displayName;
+        cache = CacheBuilder.newBuilder().maximumSize(100).build();
+        stats = new Stats();
+    }
+
+    public Store<T> createCachedStore(final Object id) {
+        return new SimpleStore<T>(cache, id, stats);
+    }
+
+    public void close() {
+        LOG.debug(displayName + " cache closed. Cache reads: "
+                + stats.readsFromCache + ", disk reads: "
+                + stats.readsFromDisk + " (avg: " + prettyTime(stats.getDiskReadsAvgMs()) + ", total: " + prettyTime(stats.diskReadsTotalMs.get()) + ")");
+    }
+
+    private static class Stats {
+        private final AtomicLong diskReadsTotalMs = new AtomicLong();
+        private final AtomicLong readsFromCache = new AtomicLong();
+        private final AtomicLong readsFromDisk = new AtomicLong();
+
+        public void readFromDisk(long start) {
+            long duration = System.currentTimeMillis() - start;
+            readsFromDisk.incrementAndGet();
+            diskReadsTotalMs.addAndGet(duration);
+        }
+
+        public void readFromCache() {
+            readsFromCache.incrementAndGet();
+        }
+
+        public long getDiskReadsAvgMs() {
+            if (readsFromDisk.get() == 0) {
+                return 0;
+            }
+            return diskReadsTotalMs.get() / readsFromDisk.get();
+        }
+    }
+
+    private static class SimpleStore<T> implements Store<T> {
+        private Cache<Object, T> cache;
+        private final Object id;
+        private Stats stats;
+
+        public SimpleStore(Cache<Object, T> cache, Object id, Stats stats) {
+            this.cache = cache;
+            this.id = id;
+            this.stats = stats;
+        }
+
+        public T load(Factory<T> createIfNotPresent) {
+            T out = cache.getIfPresent(id);
+            if (out != null) {
+                stats.readFromCache();
+                return out;
+            }
+            long start = System.currentTimeMillis();
+            T value = createIfNotPresent.create();
+            stats.readFromDisk(start);
+            cache.put(id, value);
+            return value;
+        }
+    }
+}
diff --git a/subprojects/core-impl/src/main/groovy/org/gradle/api/internal/artifacts/ivyservice/resolveengine/store/DefaultBinaryStore.java b/subprojects/core-impl/src/main/groovy/org/gradle/api/internal/artifacts/ivyservice/resolveengine/store/DefaultBinaryStore.java
new file mode 100644
index 0000000..247bbac
--- /dev/null
+++ b/subprojects/core-impl/src/main/groovy/org/gradle/api/internal/artifacts/ivyservice/resolveengine/store/DefaultBinaryStore.java
@@ -0,0 +1,138 @@
+/*
+ * Copyright 2013 the original author or authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY 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.resolveengine.store;
+
+import org.gradle.api.internal.cache.BinaryStore;
+import org.gradle.util.GStreamUtil;
+
+import java.io.*;
+
+import static org.gradle.internal.UncheckedException.throwAsUncheckedException;
+
+class DefaultBinaryStore implements BinaryStore {
+    private File file;
+    private DataOutputStream outputStream;
+    private int offset = -1;
+
+    public DefaultBinaryStore(File file) {
+        this.file = file;
+    }
+
+    public void write(WriteAction write) {
+        if (outputStream == null) {
+            try {
+                outputStream = new DataOutputStream(new BufferedOutputStream(new FileOutputStream(file)));
+            } catch (FileNotFoundException e) {
+                throw throwAsUncheckedException(e);
+            }
+        }
+        if (offset == -1) {
+            offset = outputStream.size();
+            if (offset == Integer.MAX_VALUE) {
+                throw new IllegalStateException("Unable to write to binary store. "
+                        + "The bytes offset has reached a point where using it is unsafe. Please report this error.");
+            }
+        }
+        try {
+            write.write(outputStream);
+        } catch (IOException e) {
+            throw new RuntimeException("Problems writing to " + diagnose(), e);
+        }
+    }
+
+    private String diagnose() {
+        return toString() + " (exist: " + file.exists() + ")";
+    }
+
+    public String toString() {
+        return "Binary store in " + file;
+    }
+
+    public BinaryData done() {
+        try {
+            if (outputStream != null) {
+                outputStream.flush();
+            }
+            return new SimpleBinaryData(file, offset, diagnose());
+        } catch (IOException e) {
+            throw new RuntimeException("Problems flushing data to " + diagnose(), e);
+        } finally {
+            offset = -1;
+        }
+    }
+
+    public void close() {
+        try {
+            if (outputStream != null) {
+                outputStream.close();
+            }
+        } catch (IOException e) {
+            throw throwAsUncheckedException(e);
+        } finally {
+            file.delete();
+            outputStream = null;
+            file = null;
+        }
+    }
+
+    File getFile() {
+        return file;
+    }
+
+    long getSize() {
+        return file.length();
+    }
+
+    private static class SimpleBinaryData implements BinaryData {
+        private DataInputStream input;
+        private File inputFile;
+        private int offset;
+        private final String sourceDescription;
+
+        public SimpleBinaryData(File inputFile, int offset, String sourceDescription) {
+            this.inputFile = inputFile;
+            this.offset = offset;
+            this.sourceDescription = sourceDescription;
+        }
+
+        public <T> T read(ReadAction<T> readAction) {
+            try {
+                if (input == null) {
+                    input = new DataInputStream(new BufferedInputStream(new FileInputStream(inputFile)));
+                    GStreamUtil.skipBytes(offset, input);
+                }
+                return readAction.read(input);
+            } catch (Exception e) {
+                throw new RuntimeException("Problems reading data from " + sourceDescription, e);
+            }
+        }
+
+        public void done() {
+            try {
+                if (input != null) {
+                    input.close();
+                }
+            } catch (IOException e) {
+                throw new RuntimeException("Problems closing the " + sourceDescription, e);
+            }
+            input = null;
+        }
+
+        public String toString() {
+            return sourceDescription;
+        }
+    }
+}
diff --git a/subprojects/core-impl/src/main/groovy/org/gradle/api/internal/artifacts/ivyservice/resolveengine/store/EncodedWriteAction.java b/subprojects/core-impl/src/main/groovy/org/gradle/api/internal/artifacts/ivyservice/resolveengine/store/EncodedWriteAction.java
new file mode 100644
index 0000000..ad4b560
--- /dev/null
+++ b/subprojects/core-impl/src/main/groovy/org/gradle/api/internal/artifacts/ivyservice/resolveengine/store/EncodedWriteAction.java
@@ -0,0 +1,31 @@
+/*
+ * Copyright 2013 the original author or authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY 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.resolveengine.store;
+
+import org.gradle.api.internal.cache.BinaryStore;
+import org.gradle.messaging.serialize.FlushableEncoder;
+import org.gradle.messaging.serialize.OutputStreamBackedEncoder;
+
+import java.io.DataOutputStream;
+import java.io.IOException;
+
+public abstract class EncodedWriteAction implements BinaryStore.WriteAction {
+    public final void write(DataOutputStream output) throws IOException {
+        write(new OutputStreamBackedEncoder(output));
+    }
+
+    public abstract void write(FlushableEncoder encoder) throws IOException;
+}
diff --git a/subprojects/core-impl/src/main/groovy/org/gradle/api/internal/artifacts/ivyservice/resolveengine/store/ResolutionResultsStoreFactory.java b/subprojects/core-impl/src/main/groovy/org/gradle/api/internal/artifacts/ivyservice/resolveengine/store/ResolutionResultsStoreFactory.java
new file mode 100644
index 0000000..26c792a
--- /dev/null
+++ b/subprojects/core-impl/src/main/groovy/org/gradle/api/internal/artifacts/ivyservice/resolveengine/store/ResolutionResultsStoreFactory.java
@@ -0,0 +1,103 @@
+/*
+ * Copyright 2013 the original author or authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY 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.resolveengine.store;
+
+import org.gradle.api.internal.cache.Store;
+import org.gradle.api.internal.file.TemporaryFileProvider;
+import org.gradle.api.logging.Logger;
+import org.gradle.api.logging.Logging;
+import org.gradle.internal.CompositeStoppable;
+import org.gradle.util.Clock;
+
+import java.io.Closeable;
+import java.io.File;
+import java.io.IOException;
+import java.util.HashMap;
+import java.util.Map;
+
+public class ResolutionResultsStoreFactory implements Closeable {
+    private final static Logger LOG = Logging.getLogger(ResolutionResultsStoreFactory.class);
+    private static final int DEFAULT_MAX_SIZE = 2000000000; //2 gigs
+
+    private final TemporaryFileProvider temp;
+    private int maxSize;
+
+    private CachedStoreFactory oldModelCache;
+    private CachedStoreFactory newModelCache;
+
+    public ResolutionResultsStoreFactory(TemporaryFileProvider temp) {
+        this(temp, DEFAULT_MAX_SIZE);
+    }
+
+    /**
+     * @param temp
+     * @param maxSize - indicates the approx. maximum size of the binary store that will trigger rolling of the file
+     */
+    ResolutionResultsStoreFactory(TemporaryFileProvider temp, int maxSize) {
+        this.temp = temp;
+        this.maxSize = maxSize;
+    }
+
+    private final Map<String, DefaultBinaryStore> stores = new HashMap<String, DefaultBinaryStore>();
+    private final CompositeStoppable cleanUpLater = new CompositeStoppable();
+
+    public DefaultBinaryStore createBinaryStore(String id) {
+        String storeKey = Thread.currentThread().getId() + id; //one store per thread
+        DefaultBinaryStore store = stores.get(storeKey);
+        if (store == null || isFull(store)) {
+            File storeFile = temp.createTemporaryFile("gradle", ".bin");
+            storeFile.deleteOnExit();
+            store = new DefaultBinaryStore(storeFile);
+            stores.put(storeKey, store);
+            cleanUpLater.add(store);
+        }
+        return store;
+    }
+
+    //offset based implementation is only safe up to certain figure
+    //because of the int max value
+    //for large streams/files (huge builds), we need to roll the file
+    //otherwise the stream.size() returns max integer and the offset is no longer correct
+    private boolean isFull(DefaultBinaryStore store) {
+        return store.getSize() > maxSize;
+    }
+
+    public void close() throws IOException {
+        Clock clock = new Clock();
+        cleanUpLater.stop();
+        LOG.debug("Deleted {} resolution results binary files in {}", stores.size(), clock.getTime());
+        oldModelCache = null;
+        newModelCache = null;
+        stores.clear();
+    }
+
+    public <T> Store<T> createOldModelCache(Object id) {
+        if (oldModelCache == null) {
+            oldModelCache = new CachedStoreFactory("Resolution result");
+            cleanUpLater.add(oldModelCache);
+        }
+        return oldModelCache.createCachedStore(id);
+    }
+
+    public <T> Store<T> createNewModelCache(Object id) {
+        if (newModelCache == null) {
+            newModelCache = new CachedStoreFactory("Resolved configuration");
+            cleanUpLater.add(newModelCache);
+        }
+        return newModelCache.createCachedStore(id);
+    }
+}
\ No newline at end of file
diff --git a/subprojects/core-impl/src/main/groovy/org/gradle/api/internal/artifacts/mvnsettings/DefaultLocalMavenRepositoryLocator.java b/subprojects/core-impl/src/main/groovy/org/gradle/api/internal/artifacts/mvnsettings/DefaultLocalMavenRepositoryLocator.java
index 4f232aa..4797f5e 100644
--- a/subprojects/core-impl/src/main/groovy/org/gradle/api/internal/artifacts/mvnsettings/DefaultLocalMavenRepositoryLocator.java
+++ b/subprojects/core-impl/src/main/groovy/org/gradle/api/internal/artifacts/mvnsettings/DefaultLocalMavenRepositoryLocator.java
@@ -16,7 +16,7 @@
 package org.gradle.api.internal.artifacts.mvnsettings;
 
 import org.gradle.mvn3.org.apache.maven.settings.Settings;
-import org.gradle.mvn3.org.apache.maven.settings.building.*;
+import org.gradle.mvn3.org.apache.maven.settings.building.SettingsBuildingException;
 import org.slf4j.Logger;
 import org.slf4j.LoggerFactory;
 
@@ -25,9 +25,6 @@ import java.util.Map;
 import java.util.regex.Matcher;
 import java.util.regex.Pattern;
 
-/**
- * @author Steve Ebersole
- */
 public class DefaultLocalMavenRepositoryLocator implements LocalMavenRepositoryLocator {
     private static final Logger LOGGER = LoggerFactory.getLogger(DefaultLocalMavenRepositoryLocator.class);
     private static final Pattern PLACEHOLDER_PATTERN = Pattern.compile("\\$\\{([^\\}]*)\\}");
@@ -55,7 +52,7 @@ public class DefaultLocalMavenRepositoryLocator implements LocalMavenRepositoryL
                 return defaultLocation;
             }
         } catch (SettingsBuildingException e) {
-            throw new CannotLocateLocalMavenRepositoryException("Unable to parse local maven settings.", e);
+            throw new CannotLocateLocalMavenRepositoryException("Unable to parse local Maven settings.", e);
         }
     }
 
diff --git a/subprojects/core-impl/src/main/groovy/org/gradle/api/internal/artifacts/mvnsettings/DefaultMavenFileLocations.java b/subprojects/core-impl/src/main/groovy/org/gradle/api/internal/artifacts/mvnsettings/DefaultMavenFileLocations.java
index ff01ffc..b07ce89 100644
--- a/subprojects/core-impl/src/main/groovy/org/gradle/api/internal/artifacts/mvnsettings/DefaultMavenFileLocations.java
+++ b/subprojects/core-impl/src/main/groovy/org/gradle/api/internal/artifacts/mvnsettings/DefaultMavenFileLocations.java
@@ -22,9 +22,6 @@ import org.gradle.util.DeprecationLogger;
 
 import java.io.File;
 
-/**
-* @author Szczepan Faber, created at: 3/30/11
-*/
 public class DefaultMavenFileLocations implements MavenFileLocations {
     public File getUserMavenDir() {
         return new File(SystemProperties.getUserHome(), ".m2");
diff --git a/subprojects/core-impl/src/main/groovy/org/gradle/api/internal/artifacts/mvnsettings/DefaultMavenSettingsProvider.java b/subprojects/core-impl/src/main/groovy/org/gradle/api/internal/artifacts/mvnsettings/DefaultMavenSettingsProvider.java
index 6846eb3..04ec293 100644
--- a/subprojects/core-impl/src/main/groovy/org/gradle/api/internal/artifacts/mvnsettings/DefaultMavenSettingsProvider.java
+++ b/subprojects/core-impl/src/main/groovy/org/gradle/api/internal/artifacts/mvnsettings/DefaultMavenSettingsProvider.java
@@ -18,9 +18,6 @@ package org.gradle.api.internal.artifacts.mvnsettings;
 import org.gradle.mvn3.org.apache.maven.settings.Settings;
 import org.gradle.mvn3.org.apache.maven.settings.building.*;
 
-/**
- * @author Szczepan Faber/Steve Ebersole
- */
 public class DefaultMavenSettingsProvider implements MavenSettingsProvider {
 
     private final MavenFileLocations mavenFileLocations;
diff --git a/subprojects/core-impl/src/main/groovy/org/gradle/api/internal/artifacts/repositories/DefaultBaseRepositoryFactory.java b/subprojects/core-impl/src/main/groovy/org/gradle/api/internal/artifacts/repositories/DefaultBaseRepositoryFactory.java
index c776957..a17b580 100644
--- a/subprojects/core-impl/src/main/groovy/org/gradle/api/internal/artifacts/repositories/DefaultBaseRepositoryFactory.java
+++ b/subprojects/core-impl/src/main/groovy/org/gradle/api/internal/artifacts/repositories/DefaultBaseRepositoryFactory.java
@@ -16,53 +16,52 @@
 
 package org.gradle.api.internal.artifacts.repositories;
 
-import org.apache.ivy.core.cache.RepositoryCacheManager;
 import org.apache.ivy.core.module.id.ArtifactRevisionId;
 import org.apache.ivy.plugins.resolver.DependencyResolver;
 import org.gradle.api.InvalidUserDataException;
 import org.gradle.api.artifacts.dsl.RepositoryHandler;
 import org.gradle.api.artifacts.repositories.*;
 import org.gradle.api.internal.artifacts.BaseRepositoryFactory;
+import org.gradle.api.internal.artifacts.ModuleMetadataProcessor;
+import org.gradle.api.internal.artifacts.dsl.DefaultRepositoryHandler;
+import org.gradle.api.internal.artifacts.ivyservice.ivyresolve.parser.MetaDataParser;
 import org.gradle.api.internal.artifacts.mvnsettings.LocalMavenRepositoryLocator;
+import org.gradle.api.internal.artifacts.repositories.legacy.LegacyDependencyResolverRepositoryFactory;
 import org.gradle.api.internal.artifacts.repositories.transport.RepositoryTransportFactory;
 import org.gradle.api.internal.externalresource.local.LocallyAvailableResourceFinder;
 import org.gradle.api.internal.file.FileResolver;
 import org.gradle.internal.reflect.Instantiator;
-import org.gradle.logging.ProgressLoggerFactory;
 import org.gradle.util.ConfigureUtil;
 
 import java.io.File;
 import java.util.Map;
 
-/**
- * @author Hans Dockter
- */
 public class DefaultBaseRepositoryFactory implements BaseRepositoryFactory {
     private final LocalMavenRepositoryLocator localMavenRepositoryLocator;
     private final FileResolver fileResolver;
     private final Instantiator instantiator;
     private final RepositoryTransportFactory transportFactory;
     private final LocallyAvailableResourceFinder<ArtifactRevisionId> locallyAvailableResourceFinder;
-    private final ProgressLoggerFactory progressLoggerFactory;
-    private final RepositoryCacheManager localCacheManager;
-    private final RepositoryCacheManager downloadingCacheManager;
+    private final MetaDataParser metaDataParser;
+    private final ModuleMetadataProcessor metadataProcessor;
+    private final LegacyDependencyResolverRepositoryFactory legacyDependencyResolverRepositoryFactory;
 
     public DefaultBaseRepositoryFactory(LocalMavenRepositoryLocator localMavenRepositoryLocator,
                                         FileResolver fileResolver,
                                         Instantiator instantiator,
                                         RepositoryTransportFactory transportFactory,
                                         LocallyAvailableResourceFinder<ArtifactRevisionId> locallyAvailableResourceFinder,
-                                        ProgressLoggerFactory progressLoggerFactory,
-                                        RepositoryCacheManager localCacheManager,
-                                        RepositoryCacheManager downloadingCacheManager) {
+                                        MetaDataParser metaDataParser,
+                                        ModuleMetadataProcessor metadataProcessor,
+                                        LegacyDependencyResolverRepositoryFactory legacyDependencyResolverRepositoryFactory) {
         this.localMavenRepositoryLocator = localMavenRepositoryLocator;
         this.fileResolver = fileResolver;
         this.instantiator = instantiator;
         this.transportFactory = transportFactory;
         this.locallyAvailableResourceFinder = locallyAvailableResourceFinder;
-        this.progressLoggerFactory = progressLoggerFactory;
-        this.localCacheManager = localCacheManager;
-        this.downloadingCacheManager = downloadingCacheManager;
+        this.metaDataParser = metaDataParser;
+        this.metadataProcessor = metadataProcessor;
+        this.legacyDependencyResolverRepositoryFactory = legacyDependencyResolverRepositoryFactory;
     }
 
     public ArtifactRepository createRepository(Object userDescription) {
@@ -81,17 +80,14 @@ public class DefaultBaseRepositoryFactory implements BaseRepositoryFactory {
             return repository;
         }
 
-        DependencyResolver result;
         if (userDescription instanceof DependencyResolver) {
-            result = (DependencyResolver) userDescription;
-        } else {
-            throw new InvalidUserDataException(String.format("Cannot create a DependencyResolver instance from %s", userDescription));
+            return legacyDependencyResolverRepositoryFactory.createRepository((DependencyResolver) userDescription);
         }
-        return new CustomResolverArtifactRepository(result, progressLoggerFactory, localCacheManager, downloadingCacheManager);
+        throw new InvalidUserDataException(String.format("Cannot create a DependencyResolver instance from %s", userDescription));
     }
 
     public FlatDirectoryArtifactRepository createFlatDirRepository() {
-        return instantiator.newInstance(DefaultFlatDirArtifactRepository.class, fileResolver, localCacheManager);
+        return instantiator.newInstance(DefaultFlatDirArtifactRepository.class, fileResolver, transportFactory, locallyAvailableResourceFinder, metaDataParser, metadataProcessor);
     }
 
     public MavenArtifactRepository createMavenLocalRepository() {
@@ -101,6 +97,12 @@ public class DefaultBaseRepositoryFactory implements BaseRepositoryFactory {
         return mavenRepository;
     }
 
+    public MavenArtifactRepository createJCenterRepository() {
+        MavenArtifactRepository mavenRepository = createMavenRepository();
+        mavenRepository.setUrl(DefaultRepositoryHandler.BINTRAY_JCENTER_URL);
+        return mavenRepository;
+    }
+
     public MavenArtifactRepository createMavenCentralRepository() {
         MavenArtifactRepository mavenRepository = createMavenRepository();
         mavenRepository.setUrl(RepositoryHandler.MAVEN_CENTRAL_URL);
@@ -109,13 +111,13 @@ public class DefaultBaseRepositoryFactory implements BaseRepositoryFactory {
 
     public IvyArtifactRepository createIvyRepository() {
         return instantiator.newInstance(DefaultIvyArtifactRepository.class, fileResolver, createPasswordCredentials(), transportFactory,
-                locallyAvailableResourceFinder, instantiator
+                locallyAvailableResourceFinder, instantiator, metaDataParser, metadataProcessor
         );
     }
 
     public MavenArtifactRepository createMavenRepository() {
         return instantiator.newInstance(DefaultMavenArtifactRepository.class, fileResolver, createPasswordCredentials(), transportFactory,
-                locallyAvailableResourceFinder
+                locallyAvailableResourceFinder, metaDataParser, metadataProcessor
         );
     }
 
@@ -130,5 +132,4 @@ public class DefaultBaseRepositoryFactory implements BaseRepositoryFactory {
     private PasswordCredentials createPasswordCredentials() {
         return instantiator.newInstance(DefaultPasswordCredentials.class);
     }
-
 }
diff --git a/subprojects/core-impl/src/main/groovy/org/gradle/api/internal/artifacts/repositories/DefaultFlatDirArtifactRepository.java b/subprojects/core-impl/src/main/groovy/org/gradle/api/internal/artifacts/repositories/DefaultFlatDirArtifactRepository.java
index 9ba4a83..d149036 100644
--- a/subprojects/core-impl/src/main/groovy/org/gradle/api/internal/artifacts/repositories/DefaultFlatDirArtifactRepository.java
+++ b/subprojects/core-impl/src/main/groovy/org/gradle/api/internal/artifacts/repositories/DefaultFlatDirArtifactRepository.java
@@ -16,13 +16,17 @@
 package org.gradle.api.internal.artifacts.repositories;
 
 import com.google.common.collect.Lists;
-import org.apache.ivy.core.cache.RepositoryCacheManager;
+import org.apache.ivy.core.module.id.ArtifactRevisionId;
 import org.apache.ivy.plugins.resolver.DependencyResolver;
-import org.apache.ivy.plugins.resolver.FileSystemResolver;
 import org.gradle.api.InvalidUserDataException;
 import org.gradle.api.artifacts.repositories.FlatDirectoryArtifactRepository;
-import org.gradle.api.internal.artifacts.ivyservice.ivyresolve.IvyAwareModuleVersionRepository;
-import org.gradle.api.internal.artifacts.ivyservice.ivyresolve.IvyDependencyResolverAdapter;
+import org.gradle.api.internal.artifacts.ModuleMetadataProcessor;
+import org.gradle.api.internal.artifacts.ModuleVersionPublisher;
+import org.gradle.api.internal.artifacts.ivyservice.ivyresolve.ConfiguredModuleVersionRepository;
+import org.gradle.api.internal.artifacts.ivyservice.ivyresolve.parser.MetaDataParser;
+import org.gradle.api.internal.artifacts.repositories.resolver.IvyResolver;
+import org.gradle.api.internal.artifacts.repositories.transport.RepositoryTransportFactory;
+import org.gradle.api.internal.externalresource.local.LocallyAvailableResourceFinder;
 import org.gradle.api.internal.file.FileResolver;
 
 import java.io.File;
@@ -34,11 +38,20 @@ import java.util.Set;
 public class DefaultFlatDirArtifactRepository extends AbstractArtifactRepository implements FlatDirectoryArtifactRepository {
     private final FileResolver fileResolver;
     private List<Object> dirs = new ArrayList<Object>();
-    private final RepositoryCacheManager localCacheManager;
+    private final RepositoryTransportFactory transportFactory;
+    private final LocallyAvailableResourceFinder<ArtifactRevisionId> locallyAvailableResourceFinder;
+    private final MetaDataParser metaDataParser;
+    private final ModuleMetadataProcessor metadataProcessor;
 
-    public DefaultFlatDirArtifactRepository(FileResolver fileResolver, RepositoryCacheManager localCacheManager) {
+    public DefaultFlatDirArtifactRepository(FileResolver fileResolver,
+                                            RepositoryTransportFactory transportFactory,
+                                            LocallyAvailableResourceFinder<ArtifactRevisionId> locallyAvailableResourceFinder,
+                                            MetaDataParser metaDataParser, ModuleMetadataProcessor metadataProcessor) {
         this.fileResolver = fileResolver;
-        this.localCacheManager = localCacheManager;
+        this.transportFactory = transportFactory;
+        this.locallyAvailableResourceFinder = locallyAvailableResourceFinder;
+        this.metaDataParser = metaDataParser;
+        this.metadataProcessor = metadataProcessor;
     }
 
     public Set<File> getDirs() {
@@ -57,28 +70,30 @@ public class DefaultFlatDirArtifactRepository extends AbstractArtifactRepository
         this.dirs.addAll(Arrays.asList(dirs));
     }
 
-    public DependencyResolver createPublisher() {
-        return createLegacyDslObject();
+    public ModuleVersionPublisher createPublisher() {
+        return createRealResolver();
     }
 
-    public IvyAwareModuleVersionRepository createResolver() {
-        return new IvyDependencyResolverAdapter(createLegacyDslObject());
+    public ConfiguredModuleVersionRepository createResolver() {
+        return createRealResolver();
     }
 
     public DependencyResolver createLegacyDslObject() {
+        IvyResolver resolver = createRealResolver();
+        return new LegacyDependencyResolver(resolver);
+    }
+
+    private IvyResolver createRealResolver() {
         Set<File> dirs = getDirs();
         if (dirs.isEmpty()) {
             throw new InvalidUserDataException("You must specify at least one directory for a flat directory repository.");
         }
 
-        FileSystemResolver resolver = new FileSystemResolver();
-        resolver.setName(getName());
+        IvyResolver resolver = new IvyResolver(getName(), transportFactory.createFileTransport(getName()), locallyAvailableResourceFinder, metaDataParser, metadataProcessor, false);
         for (File root : dirs) {
             resolver.addArtifactPattern(root.getAbsolutePath() + "/[artifact]-[revision](-[classifier]).[ext]");
             resolver.addArtifactPattern(root.getAbsolutePath() + "/[artifact](-[classifier]).[ext]");
         }
-        resolver.setValidate(false);
-        resolver.setRepositoryCacheManager(localCacheManager);
         return resolver;
     }
 
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
index 0955405..1e1e4a0 100644
--- 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
@@ -22,11 +22,14 @@ import org.gradle.api.InvalidUserDataException;
 import org.gradle.api.artifacts.repositories.IvyArtifactRepository;
 import org.gradle.api.artifacts.repositories.IvyArtifactRepositoryMetaDataProvider;
 import org.gradle.api.artifacts.repositories.PasswordCredentials;
-import org.gradle.api.internal.artifacts.ivyservice.ivyresolve.ExternalResourceResolverAdapter;
-import org.gradle.api.internal.artifacts.ivyservice.ivyresolve.IvyAwareModuleVersionRepository;
+import org.gradle.api.internal.artifacts.ModuleMetadataProcessor;
+import org.gradle.api.internal.artifacts.ModuleVersionPublisher;
+import org.gradle.api.internal.artifacts.ivyservice.ivyresolve.ConfiguredModuleVersionRepository;
+import org.gradle.api.internal.artifacts.ivyservice.ivyresolve.parser.MetaDataParser;
 import org.gradle.api.internal.artifacts.repositories.layout.*;
 import org.gradle.api.internal.artifacts.repositories.resolver.IvyResolver;
 import org.gradle.api.internal.artifacts.repositories.resolver.PatternBasedResolver;
+import org.gradle.api.internal.artifacts.repositories.transport.RepositoryTransport;
 import org.gradle.api.internal.artifacts.repositories.transport.RepositoryTransportFactory;
 import org.gradle.api.internal.externalresource.local.LocallyAvailableResourceFinder;
 import org.gradle.api.internal.file.FileResolver;
@@ -47,9 +50,12 @@ public class DefaultIvyArtifactRepository extends AbstractAuthenticationSupporte
     private final LocallyAvailableResourceFinder<ArtifactRevisionId> locallyAvailableResourceFinder;
     private final MetaDataProvider metaDataProvider;
     private final Instantiator instantiator;
+    private final MetaDataParser metaDataParser;
+    private final ModuleMetadataProcessor metadataProcessor;
 
     public DefaultIvyArtifactRepository(FileResolver fileResolver, PasswordCredentials credentials, RepositoryTransportFactory transportFactory,
-                                        LocallyAvailableResourceFinder<ArtifactRevisionId> locallyAvailableResourceFinder, Instantiator instantiator) {
+                                        LocallyAvailableResourceFinder<ArtifactRevisionId> locallyAvailableResourceFinder, Instantiator instantiator,
+                                        MetaDataParser metaDataParser, ModuleMetadataProcessor metadataProcessor) {
         super(credentials);
         this.fileResolver = fileResolver;
         this.transportFactory = transportFactory;
@@ -58,23 +64,21 @@ public class DefaultIvyArtifactRepository extends AbstractAuthenticationSupporte
         this.layout = new GradleRepositoryLayout();
         this.metaDataProvider = new MetaDataProvider();
         this.instantiator = instantiator;
+        this.metaDataParser = metaDataParser;
+        this.metadataProcessor = metadataProcessor;
     }
 
     public DependencyResolver createLegacyDslObject() {
         IvyResolver resolver = createRealResolver();
-        return new LegacyDependencyResolver(resolver, wrapResolver(resolver));
+        return new LegacyDependencyResolver(resolver);
     }
 
-    public DependencyResolver createPublisher() {
+    public ModuleVersionPublisher createPublisher() {
         return createRealResolver();
     }
 
-    public IvyAwareModuleVersionRepository createResolver() {
-        return wrapResolver(createRealResolver());
-    }
-
-    private ExternalResourceResolverAdapter wrapResolver(IvyResolver resolver) {
-        return new ExternalResourceResolverAdapter(resolver, metaDataProvider.dynamicResolve);
+    public ConfiguredModuleVersionRepository createResolver() {
+        return createRealResolver();
     }
 
     protected IvyResolver createRealResolver() {
@@ -100,14 +104,21 @@ public class DefaultIvyArtifactRepository extends AbstractAuthenticationSupporte
             throw new InvalidUserDataException("You may only specify 'file', 'http' and 'https' urls for an Ivy repository.");
         }
         if (WrapUtil.toSet("http", "https").containsAll(schemes)) {
-            return new IvyResolver(getName(), transportFactory.createHttpTransport(getName(), getCredentials()), locallyAvailableResourceFinder);
+            return createResolver(transportFactory.createHttpTransport(getName(), getCredentials()));
         }
         if (WrapUtil.toSet("file").containsAll(schemes)) {
-            return new IvyResolver(getName(), transportFactory.createFileTransport(getName()), locallyAvailableResourceFinder);
+            return createResolver(transportFactory.createFileTransport(getName()));
         }
         throw new InvalidUserDataException("You cannot mix file and http(s) urls for a single Ivy repository. Please declare 2 separate repositories.");
     }
 
+    private IvyResolver createResolver(RepositoryTransport httpTransport) {
+        return new IvyResolver(
+                getName(), httpTransport,
+                locallyAvailableResourceFinder,
+                metaDataParser, metadataProcessor, metaDataProvider.dynamicResolve);
+    }
+
     public URI getUrl() {
         return baseUrl == null ? null : fileResolver.resolveUri(baseUrl);
     }
diff --git a/subprojects/core-impl/src/main/groovy/org/gradle/api/internal/artifacts/repositories/DefaultMavenArtifactRepository.java b/subprojects/core-impl/src/main/groovy/org/gradle/api/internal/artifacts/repositories/DefaultMavenArtifactRepository.java
index 60fe1b5..b52f0f5 100644
--- a/subprojects/core-impl/src/main/groovy/org/gradle/api/internal/artifacts/repositories/DefaultMavenArtifactRepository.java
+++ b/subprojects/core-impl/src/main/groovy/org/gradle/api/internal/artifacts/repositories/DefaultMavenArtifactRepository.java
@@ -21,8 +21,10 @@ import org.apache.ivy.plugins.resolver.DependencyResolver;
 import org.gradle.api.InvalidUserDataException;
 import org.gradle.api.artifacts.repositories.MavenArtifactRepository;
 import org.gradle.api.artifacts.repositories.PasswordCredentials;
-import org.gradle.api.internal.artifacts.ivyservice.ivyresolve.ExternalResourceResolverAdapter;
-import org.gradle.api.internal.artifacts.ivyservice.ivyresolve.IvyAwareModuleVersionRepository;
+import org.gradle.api.internal.artifacts.ModuleMetadataProcessor;
+import org.gradle.api.internal.artifacts.ModuleVersionPublisher;
+import org.gradle.api.internal.artifacts.ivyservice.ivyresolve.ConfiguredModuleVersionRepository;
+import org.gradle.api.internal.artifacts.ivyservice.ivyresolve.parser.MetaDataParser;
 import org.gradle.api.internal.artifacts.repositories.resolver.MavenResolver;
 import org.gradle.api.internal.artifacts.repositories.transport.RepositoryTransport;
 import org.gradle.api.internal.artifacts.repositories.transport.RepositoryTransportFactory;
@@ -41,13 +43,18 @@ public class DefaultMavenArtifactRepository extends AbstractAuthenticationSuppor
     private Object url;
     private List<Object> additionalUrls = new ArrayList<Object>();
     private final LocallyAvailableResourceFinder<ArtifactRevisionId> locallyAvailableResourceFinder;
+    private final MetaDataParser metaDataParser;
+    private final ModuleMetadataProcessor metadataProcessor;
 
     public DefaultMavenArtifactRepository(FileResolver fileResolver, PasswordCredentials credentials, RepositoryTransportFactory transportFactory,
-                                          LocallyAvailableResourceFinder<ArtifactRevisionId> locallyAvailableResourceFinder) {
+                                          LocallyAvailableResourceFinder<ArtifactRevisionId> locallyAvailableResourceFinder, MetaDataParser metaDataParser,
+                                          ModuleMetadataProcessor metadataProcessor) {
         super(credentials);
         this.fileResolver = fileResolver;
         this.transportFactory = transportFactory;
         this.locallyAvailableResourceFinder = locallyAvailableResourceFinder;
+        this.metaDataParser = metaDataParser;
+        this.metadataProcessor = metadataProcessor;
     }
 
     public URI getUrl() {
@@ -74,21 +81,17 @@ public class DefaultMavenArtifactRepository extends AbstractAuthenticationSuppor
         additionalUrls = Lists.newArrayList(urls);
     }
 
-    public DependencyResolver createPublisher() {
+    public ModuleVersionPublisher createPublisher() {
         return createRealResolver();
     }
 
     public DependencyResolver createLegacyDslObject() {
         MavenResolver resolver = createRealResolver();
-        return new LegacyMavenResolver(resolver, wrapResolver(resolver));
+        return new LegacyMavenResolver(resolver);
     }
 
-    public IvyAwareModuleVersionRepository createResolver() {
-        return wrapResolver(createRealResolver());
-    }
-
-    private ExternalResourceResolverAdapter wrapResolver(MavenResolver resolver) {
-        return new ExternalResourceResolverAdapter(resolver, false);
+    public ConfiguredModuleVersionRepository createResolver() {
+        return createRealResolver();
     }
 
     protected MavenResolver createRealResolver() {
@@ -97,7 +100,7 @@ public class DefaultMavenArtifactRepository extends AbstractAuthenticationSuppor
             throw new InvalidUserDataException("You must specify a URL for a Maven repository.");
         }
 
-        MavenResolver resolver = new MavenResolver(getName(), rootUri, getTransport(rootUri.getScheme()), locallyAvailableResourceFinder);
+        MavenResolver resolver = new MavenResolver(getName(), rootUri, getTransport(rootUri.getScheme()), locallyAvailableResourceFinder, metaDataParser, metadataProcessor);
         for (URI repoUrl : getArtifactUrls()) {
             resolver.addArtifactLocation(repoUrl, null);
         }
diff --git a/subprojects/core-impl/src/main/groovy/org/gradle/api/internal/artifacts/repositories/FixedResolverArtifactRepository.java b/subprojects/core-impl/src/main/groovy/org/gradle/api/internal/artifacts/repositories/FixedResolverArtifactRepository.java
index d5ff8b0..7b02f40 100644
--- a/subprojects/core-impl/src/main/groovy/org/gradle/api/internal/artifacts/repositories/FixedResolverArtifactRepository.java
+++ b/subprojects/core-impl/src/main/groovy/org/gradle/api/internal/artifacts/repositories/FixedResolverArtifactRepository.java
@@ -16,7 +16,9 @@
 package org.gradle.api.internal.artifacts.repositories;
 
 import org.apache.ivy.plugins.resolver.DependencyResolver;
-import org.gradle.api.internal.artifacts.ivyservice.ivyresolve.IvyAwareModuleVersionRepository;
+import org.gradle.api.internal.artifacts.ModuleVersionPublisher;
+import org.gradle.api.internal.artifacts.ivyservice.IvyResolverBackedModuleVersionPublisher;
+import org.gradle.api.internal.artifacts.ivyservice.ivyresolve.ConfiguredModuleVersionRepository;
 import org.gradle.api.internal.artifacts.ivyservice.ivyresolve.IvyDependencyResolverAdapter;
 
 public class FixedResolverArtifactRepository extends AbstractArtifactRepository {
@@ -38,11 +40,11 @@ public class FixedResolverArtifactRepository extends AbstractArtifactRepository
         super.setName(name);
     }
 
-    public DependencyResolver createPublisher() {
-        return resolver;
+    public ModuleVersionPublisher createPublisher() {
+        return new IvyResolverBackedModuleVersionPublisher(resolver);
     }
 
-    public IvyAwareModuleVersionRepository createResolver() {
+    public ConfiguredModuleVersionRepository createResolver() {
         // Handle a repository wrapped in a resolver for backwards compatibility
         if (resolver instanceof ResolutionAwareRepository) {
             ResolutionAwareRepository resolutionAwareRepository = (ResolutionAwareRepository) resolver;
diff --git a/subprojects/core-impl/src/main/groovy/org/gradle/api/internal/artifacts/repositories/LegacyDependencyResolver.java b/subprojects/core-impl/src/main/groovy/org/gradle/api/internal/artifacts/repositories/LegacyDependencyResolver.java
index afca9f7..3bfa9a0 100644
--- a/subprojects/core-impl/src/main/groovy/org/gradle/api/internal/artifacts/repositories/LegacyDependencyResolver.java
+++ b/subprojects/core-impl/src/main/groovy/org/gradle/api/internal/artifacts/repositories/LegacyDependencyResolver.java
@@ -31,15 +31,17 @@ import org.apache.ivy.core.search.OrganisationEntry;
 import org.apache.ivy.core.search.RevisionEntry;
 import org.apache.ivy.plugins.latest.LatestStrategy;
 import org.apache.ivy.plugins.namespace.Namespace;
+import org.apache.ivy.plugins.resolver.BasicResolver;
 import org.apache.ivy.plugins.resolver.DependencyResolver;
 import org.apache.ivy.plugins.resolver.ResolverSettings;
 import org.apache.ivy.plugins.resolver.util.ResolvedResource;
-import org.gradle.api.internal.artifacts.ivyservice.ivyresolve.IvyAwareModuleVersionRepository;
+import org.gradle.api.internal.artifacts.ivyservice.ivyresolve.ConfiguredModuleVersionRepository;
 import org.gradle.api.internal.artifacts.repositories.resolver.ExternalResourceResolver;
 
 import java.io.File;
 import java.io.IOException;
 import java.text.ParseException;
+import java.util.Arrays;
 import java.util.List;
 import java.util.Map;
 
@@ -49,11 +51,10 @@ import java.util.Map;
  */
 public class LegacyDependencyResolver implements DependencyResolver, ResolutionAwareRepository {
     private final ExternalResourceResolver resolver;
-    private final IvyAwareModuleVersionRepository repository;
+    private ResolverSettings settings;
 
-    public LegacyDependencyResolver(ExternalResourceResolver resolver, IvyAwareModuleVersionRepository repository) {
+    public LegacyDependencyResolver(ExternalResourceResolver resolver) {
         this.resolver = resolver;
-        this.repository = repository;
     }
 
     public String getName() {
@@ -68,16 +69,16 @@ public class LegacyDependencyResolver implements DependencyResolver, ResolutionA
         return resolver.toString();
     }
 
-    public IvyAwareModuleVersionRepository createResolver() {
-        return repository;
+    public ConfiguredModuleVersionRepository createResolver() {
+        return resolver;
     }
 
-    public void setSettings(ResolverSettings ivy) {
-        resolver.setSettings(ivy);
+    public void setSettings(ResolverSettings settings) {
+        this.settings = settings;
     }
 
     public ResolverSettings getSettings() {
-        return resolver.getSettings();
+        return settings;
     }
 
     public ResolvedModuleRevision getDependency(DependencyDescriptor dd, ResolveData data) throws ParseException {
@@ -230,7 +231,16 @@ public class LegacyDependencyResolver implements DependencyResolver, ResolutionA
      * @param descriptorRule the descriptor rule to use with this resolver.
      */
     public void setDescriptor(String descriptorRule) {
-        resolver.setDescriptor(descriptorRule);
+        if (BasicResolver.DESCRIPTOR_REQUIRED.equals(descriptorRule)) {
+            setAllownomd(false);
+        } else if (BasicResolver.DESCRIPTOR_OPTIONAL.equals(descriptorRule)) {
+            setAllownomd(true);
+        } else {
+            throw new IllegalArgumentException(
+                "unknown descriptor rule '" + descriptorRule
+                + "'. Allowed rules are: "
+                + Arrays.asList(BasicResolver.DESCRIPTOR_REQUIRED, BasicResolver.DESCRIPTOR_OPTIONAL));
+        }
     }
 
     public String[] getChecksumAlgorithms() {
@@ -242,19 +252,19 @@ public class LegacyDependencyResolver implements DependencyResolver, ResolutionA
     }
 
     public LatestStrategy getLatestStrategy() {
-        return resolver.getLatestStrategy();
+        throw new UnsupportedOperationException("getLatestStrategy");
     }
 
     public void setLatestStrategy(LatestStrategy latestStrategy) {
-        resolver.setLatestStrategy(latestStrategy);
+        throw new UnsupportedOperationException("setLatestStrategy");
     }
 
-    public void setLatest(String strategyName) {
-        resolver.setLatest(strategyName);
+    public String getLatest() {
+        throw new UnsupportedOperationException("getLatest");
     }
 
-    public String getLatest() {
-        return resolver.getLatest();
+    public void setLatest(String strategyName) {
+        throw new UnsupportedOperationException("setLatest");
     }
 
     public void setChangingMatcher(String changingMatcherName) {
@@ -274,7 +284,7 @@ public class LegacyDependencyResolver implements DependencyResolver, ResolutionA
     }
 
     public void setCheckmodified(boolean check) {
-        resolver.setCheckmodified(check);
+        throw new UnsupportedOperationException();
     }
 
     public RepositoryCacheManager getRepositoryCacheManager() {
diff --git a/subprojects/core-impl/src/main/groovy/org/gradle/api/internal/artifacts/repositories/LegacyMavenResolver.java b/subprojects/core-impl/src/main/groovy/org/gradle/api/internal/artifacts/repositories/LegacyMavenResolver.java
index f409088..0810786 100644
--- a/subprojects/core-impl/src/main/groovy/org/gradle/api/internal/artifacts/repositories/LegacyMavenResolver.java
+++ b/subprojects/core-impl/src/main/groovy/org/gradle/api/internal/artifacts/repositories/LegacyMavenResolver.java
@@ -16,14 +16,13 @@
 
 package org.gradle.api.internal.artifacts.repositories;
 
-import org.gradle.api.internal.artifacts.ivyservice.ivyresolve.IvyAwareModuleVersionRepository;
 import org.gradle.api.internal.artifacts.repositories.resolver.MavenResolver;
 
 public class LegacyMavenResolver extends LegacyDependencyResolver {
     private final MavenResolver resolver;
 
-    public LegacyMavenResolver(MavenResolver resolver, IvyAwareModuleVersionRepository repository) {
-        super(resolver, repository);
+    public LegacyMavenResolver(MavenResolver resolver) {
+        super(resolver);
         this.resolver = resolver;
     }
 
diff --git a/subprojects/core-impl/src/main/groovy/org/gradle/api/internal/artifacts/repositories/ResolutionAwareRepository.java b/subprojects/core-impl/src/main/groovy/org/gradle/api/internal/artifacts/repositories/ResolutionAwareRepository.java
index ceb2598..d86d2db 100644
--- a/subprojects/core-impl/src/main/groovy/org/gradle/api/internal/artifacts/repositories/ResolutionAwareRepository.java
+++ b/subprojects/core-impl/src/main/groovy/org/gradle/api/internal/artifacts/repositories/ResolutionAwareRepository.java
@@ -16,8 +16,8 @@
 
 package org.gradle.api.internal.artifacts.repositories;
 
-import org.gradle.api.internal.artifacts.ivyservice.ivyresolve.IvyAwareModuleVersionRepository;
+import org.gradle.api.internal.artifacts.ivyservice.ivyresolve.ConfiguredModuleVersionRepository;
 
 public interface ResolutionAwareRepository {
-    IvyAwareModuleVersionRepository createResolver();
+    ConfiguredModuleVersionRepository createResolver();
 }
diff --git a/subprojects/core-impl/src/main/groovy/org/gradle/api/internal/artifacts/repositories/cachemanager/AbstractRepositoryArtifactCache.java b/subprojects/core-impl/src/main/groovy/org/gradle/api/internal/artifacts/repositories/cachemanager/AbstractRepositoryArtifactCache.java
new file mode 100644
index 0000000..a946036
--- /dev/null
+++ b/subprojects/core-impl/src/main/groovy/org/gradle/api/internal/artifacts/repositories/cachemanager/AbstractRepositoryArtifactCache.java
@@ -0,0 +1,20 @@
+/*
+ * Copyright 2013 the original author or authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY 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.cachemanager;
+
+abstract class AbstractRepositoryArtifactCache implements RepositoryArtifactCache {
+
+}
diff --git a/subprojects/core-impl/src/main/groovy/org/gradle/api/internal/artifacts/repositories/cachemanager/AbstractRepositoryCacheManager.java b/subprojects/core-impl/src/main/groovy/org/gradle/api/internal/artifacts/repositories/cachemanager/AbstractRepositoryCacheManager.java
deleted file mode 100644
index d87c553..0000000
--- a/subprojects/core-impl/src/main/groovy/org/gradle/api/internal/artifacts/repositories/cachemanager/AbstractRepositoryCacheManager.java
+++ /dev/null
@@ -1,86 +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.api.internal.artifacts.repositories.cachemanager;
-
-import org.apache.ivy.core.cache.ArtifactOrigin;
-import org.apache.ivy.core.cache.CacheMetadataOptions;
-import org.apache.ivy.core.cache.ModuleDescriptorWriter;
-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.descriptor.ModuleDescriptor;
-import org.apache.ivy.core.module.id.ModuleRevisionId;
-import org.apache.ivy.core.resolve.ResolvedModuleRevision;
-import org.apache.ivy.core.settings.IvySettings;
-import org.apache.ivy.plugins.parser.ModuleDescriptorParser;
-import org.apache.ivy.plugins.parser.ParserSettings;
-import org.apache.ivy.plugins.repository.Resource;
-import org.apache.ivy.plugins.resolver.DependencyResolver;
-import org.apache.ivy.plugins.resolver.util.ResolvedResource;
-import org.gradle.api.internal.artifacts.ivyservice.ivyresolve.IvyContextualiser;
-import org.gradle.api.internal.artifacts.ivyservice.ivyresolve.parser.ModuleScopedParserSettings;
-import org.gradle.api.internal.artifacts.ivyservice.ivyresolve.parser.ParserRegistry;
-import org.gradle.internal.UncheckedException;
-
-import java.io.File;
-import java.io.IOException;
-import java.net.URL;
-import java.text.ParseException;
-
-abstract class AbstractRepositoryCacheManager implements RepositoryCacheManager {
-    protected final String name;
-    private final ParserRegistry parserRegistry = new ParserRegistry();
-
-    public AbstractRepositoryCacheManager(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 void originalToCachedModuleDescriptor(DependencyResolver resolver, ResolvedResource originalMetadataRef, Artifact requestedMetadataArtifact, ResolvedModuleRevision rmr, ModuleDescriptorWriter writer) {
-    }
-
-    public void clean() {
-    }
-
-    public void saveResolvedRevision(ModuleRevisionId dynamicMrid, String revision) {
-    }
-
-    protected ModuleDescriptor parseModuleDescriptor(DependencyResolver resolver, Artifact moduleArtifact, CacheMetadataOptions options, File artifactFile, Resource resource) throws ParseException {
-        ModuleRevisionId moduleRevisionId = moduleArtifact.getId().getModuleRevisionId();
-        try {
-            IvySettings ivySettings = IvyContextualiser.getIvyContext().getSettings();
-            ParserSettings parserSettings = new ModuleScopedParserSettings(ivySettings, resolver, moduleRevisionId);
-            ModuleDescriptorParser parser = parserRegistry.forResource(resource);
-            return parser.parseDescriptor(parserSettings, new URL(artifactFile.toURI().toASCIIString()), resource, options.isValidate());
-        } catch (IOException e) {
-            throw UncheckedException.throwAsUncheckedException(e);
-        }
-    }
-}
diff --git a/subprojects/core-impl/src/main/groovy/org/gradle/api/internal/artifacts/repositories/cachemanager/DownloadingRepositoryArtifactCache.java b/subprojects/core-impl/src/main/groovy/org/gradle/api/internal/artifacts/repositories/cachemanager/DownloadingRepositoryArtifactCache.java
new file mode 100644
index 0000000..3c26dfa
--- /dev/null
+++ b/subprojects/core-impl/src/main/groovy/org/gradle/api/internal/artifacts/repositories/cachemanager/DownloadingRepositoryArtifactCache.java
@@ -0,0 +1,73 @@
+/*
+ * Copyright 2013 the original author or authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY 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.cachemanager;
+
+import org.apache.ivy.core.module.id.ArtifactRevisionId;
+import org.gradle.api.internal.artifacts.ivyservice.CacheLockingManager;
+import org.gradle.api.internal.externalresource.DefaultLocallyAvailableExternalResource;
+import org.gradle.api.internal.externalresource.ExternalResource;
+import org.gradle.api.internal.externalresource.LocallyAvailableExternalResource;
+import org.gradle.api.internal.externalresource.cached.CachedExternalResourceIndex;
+import org.gradle.api.internal.externalresource.metadata.ExternalResourceMetaData;
+import org.gradle.api.internal.file.TemporaryFileProvider;
+import org.gradle.internal.Factory;
+import org.gradle.internal.filestore.FileStore;
+import org.gradle.internal.resource.local.LocallyAvailableResource;
+
+import java.io.File;
+import java.io.IOException;
+
+/**
+ * A cache manager for remote repositories, that downloads files and stores them in the FileStore provided.
+ */
+public class DownloadingRepositoryArtifactCache implements RepositoryArtifactCache {
+
+    private final FileStore<ArtifactRevisionId> fileStore;
+    private final CachedExternalResourceIndex<String> artifactUrlCachedResolutionIndex;
+    private final TemporaryFileProvider temporaryFileProvider;
+    private final CacheLockingManager cacheLockingManager;
+
+    public DownloadingRepositoryArtifactCache(FileStore<ArtifactRevisionId> fileStore, CachedExternalResourceIndex<String> artifactUrlCachedResolutionIndex,
+                                              TemporaryFileProvider temporaryFileProvider, CacheLockingManager cacheLockingManager) {
+        this.fileStore = fileStore;
+        this.artifactUrlCachedResolutionIndex = artifactUrlCachedResolutionIndex;
+        this.temporaryFileProvider = temporaryFileProvider;
+        this.cacheLockingManager = cacheLockingManager;
+    }
+
+    public boolean isLocal() {
+        return false;
+    }
+
+    public LocallyAvailableExternalResource downloadAndCacheArtifactFile(final ArtifactRevisionId artifactId, ExternalResourceDownloader resourceDownloader, final ExternalResource resource) throws IOException {
+        final File tmpFile = temporaryFileProvider.createTemporaryFile("gradle_download", "bin");
+        try {
+            resourceDownloader.download(resource, tmpFile);
+            return cacheLockingManager.useCache(String.format("Store %s", artifactId), new Factory<LocallyAvailableExternalResource>() {
+                public LocallyAvailableExternalResource create() {
+                    LocallyAvailableResource cachedResource = fileStore.move(artifactId, tmpFile);
+                    File fileInFileStore = cachedResource.getFile();
+                    ExternalResourceMetaData metaData = resource.getMetaData();
+                    artifactUrlCachedResolutionIndex.store(metaData.getLocation(), fileInFileStore, metaData);
+                    return new DefaultLocallyAvailableExternalResource(resource.getName(), cachedResource, metaData);
+                }
+            });
+        } finally {
+            tmpFile.delete();
+        }
+    }
+
+}
diff --git a/subprojects/core-impl/src/main/groovy/org/gradle/api/internal/artifacts/repositories/cachemanager/DownloadingRepositoryCacheManager.java b/subprojects/core-impl/src/main/groovy/org/gradle/api/internal/artifacts/repositories/cachemanager/DownloadingRepositoryCacheManager.java
deleted file mode 100644
index 94c9032..0000000
--- a/subprojects/core-impl/src/main/groovy/org/gradle/api/internal/artifacts/repositories/cachemanager/DownloadingRepositoryCacheManager.java
+++ /dev/null
@@ -1,164 +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.api.internal.artifacts.repositories.cachemanager;
-
-import org.apache.ivy.core.cache.ArtifactOrigin;
-import org.apache.ivy.core.cache.CacheDownloadOptions;
-import org.apache.ivy.core.cache.CacheMetadataOptions;
-import org.apache.ivy.core.cache.DownloadListener;
-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.ArtifactRevisionId;
-import org.apache.ivy.core.report.ArtifactDownloadReport;
-import org.apache.ivy.core.report.DownloadStatus;
-import org.apache.ivy.core.report.MetadataArtifactDownloadReport;
-import org.apache.ivy.core.resolve.ResolvedModuleRevision;
-import org.apache.ivy.plugins.repository.ArtifactResourceResolver;
-import org.apache.ivy.plugins.repository.Resource;
-import org.apache.ivy.plugins.repository.ResourceDownloader;
-import org.apache.ivy.plugins.resolver.DependencyResolver;
-import org.apache.ivy.plugins.resolver.util.ResolvedResource;
-import org.apache.ivy.util.Message;
-import org.gradle.api.internal.artifacts.ivyservice.CacheLockingManager;
-import org.gradle.api.internal.externalresource.ExternalResource;
-import org.gradle.api.internal.externalresource.cached.CachedExternalResourceIndex;
-import org.gradle.api.internal.externalresource.metadata.ExternalResourceMetaData;
-import org.gradle.api.internal.file.TemporaryFileProvider;
-import org.gradle.api.internal.filestore.FileStore;
-import org.gradle.api.internal.filestore.FileStoreEntry;
-import org.gradle.internal.Factory;
-
-import java.io.File;
-import java.io.IOException;
-import java.text.ParseException;
-
-/**
- * A cache manager for remote repositories, that downloads files and stores them in the FileStore provided.
- */
-public class DownloadingRepositoryCacheManager extends AbstractRepositoryCacheManager {
-    private final FileStore<ArtifactRevisionId> fileStore;
-    private final CachedExternalResourceIndex<String> artifactUrlCachedResolutionIndex;
-    private final TemporaryFileProvider temporaryFileProvider;
-    private final CacheLockingManager cacheLockingManager;
-
-    public DownloadingRepositoryCacheManager(String name, FileStore<ArtifactRevisionId> fileStore, CachedExternalResourceIndex<String> artifactUrlCachedResolutionIndex,
-                                             TemporaryFileProvider temporaryFileProvider, CacheLockingManager cacheLockingManager) {
-        super(name);
-        this.fileStore = fileStore;
-        this.artifactUrlCachedResolutionIndex = artifactUrlCachedResolutionIndex;
-        this.temporaryFileProvider = temporaryFileProvider;
-        this.cacheLockingManager = cacheLockingManager;
-    }
-
-    public EnhancedArtifactDownloadReport download(Artifact artifact, ArtifactResourceResolver resourceResolver,
-                                                   ResourceDownloader resourceDownloader, CacheDownloadOptions options) {
-        EnhancedArtifactDownloadReport adr = new EnhancedArtifactDownloadReport(artifact);
-
-        DownloadListener listener = options.getListener();
-        if (listener != null) {
-            listener.needArtifact(this, artifact);
-        }
-
-        long start = System.currentTimeMillis();
-        try {
-            ResolvedResource artifactRef = resourceResolver.resolve(artifact);
-            if (artifactRef != null) {
-                final Resource resource = artifactRef.getResource();
-                ArtifactOrigin origin = new ArtifactOrigin(artifact, resource.isLocal(), resource.getName());
-                if (listener != null) {
-                    listener.startArtifactDownload(this, artifactRef, artifact, origin);
-                }
-
-                File artifactFile = downloadArtifactFile(artifact, resourceDownloader, artifactRef);
-
-                adr.setDownloadTimeMillis(System.currentTimeMillis() - start);
-                adr.setSize(artifactFile.length());
-                adr.setDownloadStatus(DownloadStatus.SUCCESSFUL);
-                adr.setArtifactOrigin(origin);
-                adr.setLocalFile(artifactFile);
-            } else {
-                adr.setDownloadTimeMillis(System.currentTimeMillis() - start);
-                adr.setDownloadStatus(DownloadStatus.FAILED);
-                adr.setDownloadDetails(ArtifactDownloadReport.MISSING_ARTIFACT);
-            }
-        } catch (Throwable throwable) {
-            adr.setDownloadTimeMillis(System.currentTimeMillis() - start);
-            adr.failed(throwable);
-        }
-        if (listener != null) {
-            listener.endArtifactDownload(this, artifact, adr, adr.getLocalFile());
-        }
-        return adr;
-    }
-
-    private File downloadArtifactFile(final Artifact artifact, final ResourceDownloader resourceDownloader, final ResolvedResource artifactRef) throws IOException {
-        final Resource resource = artifactRef.getResource();
-        final File tmpFile = temporaryFileProvider.createTemporaryFile("gradle_download", "bin");
-        try {
-            resourceDownloader.download(artifact, resource, tmpFile);
-            return cacheLockingManager.useCache(String.format("Store %s", artifact), new Factory<File>() {
-                public File create() {
-                    FileStoreEntry fileStoreEntry = fileStore.move(artifact.getId(), tmpFile);
-                    File fileInFileStore = fileStoreEntry.getFile();
-                    if (resource instanceof ExternalResource) {
-                        ExternalResource externalResource = (ExternalResource) resource;
-                        ExternalResourceMetaData metaData = externalResource.getMetaData();
-                        artifactUrlCachedResolutionIndex.store(metaData.getLocation(), fileInFileStore, metaData);
-                    }
-                    return fileInFileStore;
-                }
-            });
-        } finally {
-            tmpFile.delete();
-        }
-    }
-
-    public ResolvedModuleRevision cacheModuleDescriptor(DependencyResolver resolver, final ResolvedResource resolvedResource, DependencyDescriptor dd, Artifact moduleArtifact, ResourceDownloader downloader, CacheMetadataOptions options) throws ParseException {
-        if (!moduleArtifact.isMetadata()) {
-            return null;
-        }
-
-        ArtifactResourceResolver artifactResourceResolver = new ArtifactResourceResolver() {
-            public ResolvedResource resolve(Artifact artifact) {
-                return resolvedResource;
-            }
-        };
-        ArtifactDownloadReport report = download(moduleArtifact, artifactResourceResolver, downloader, new CacheDownloadOptions().setListener(options.getListener()).setForce(true));
-
-        if (report.getDownloadStatus() == DownloadStatus.FAILED) {
-            Message.warn("problem while downloading module descriptor: " + resolvedResource.getResource()
-                    + ": " + report.getDownloadDetails()
-                    + " (" + report.getDownloadTimeMillis() + "ms)");
-            return null;
-        }
-
-        ModuleDescriptor md = parseModuleDescriptor(resolver, moduleArtifact, options, report.getLocalFile(), resolvedResource.getResource());
-        Message.debug("\t" + getName() + ": parsed downloaded md file for " + moduleArtifact.getModuleRevisionId() + "; parsed=" + md.getModuleRevisionId());
-
-        MetadataArtifactDownloadReport madr = new MetadataArtifactDownloadReport(md.getMetadataArtifact());
-        madr.setSearched(true);
-        madr.setDownloadStatus(report.getDownloadStatus());
-        madr.setDownloadDetails(report.getDownloadDetails());
-        madr.setArtifactOrigin(report.getArtifactOrigin());
-        madr.setDownloadTimeMillis(report.getDownloadTimeMillis());
-        madr.setOriginalLocalFile(report.getLocalFile());
-        madr.setSize(report.getSize());
-
-        return new ResolvedModuleRevision(resolver, resolver, md, madr);
-    }
-
-}
diff --git a/subprojects/core-impl/src/main/groovy/org/gradle/api/internal/artifacts/repositories/cachemanager/EnhancedArtifactDownloadReport.java b/subprojects/core-impl/src/main/groovy/org/gradle/api/internal/artifacts/repositories/cachemanager/EnhancedArtifactDownloadReport.java
deleted file mode 100644
index 3d270bf..0000000
--- a/subprojects/core-impl/src/main/groovy/org/gradle/api/internal/artifacts/repositories/cachemanager/EnhancedArtifactDownloadReport.java
+++ /dev/null
@@ -1,38 +0,0 @@
-/*
- * Copyright 2012 the original author or authors.
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- *      http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY 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.cachemanager;
-
-import org.apache.ivy.core.module.descriptor.Artifact;
-import org.apache.ivy.core.report.ArtifactDownloadReport;
-import org.apache.ivy.core.report.DownloadStatus;
-
-public class EnhancedArtifactDownloadReport extends ArtifactDownloadReport {
-    private Throwable failure;
-
-    public EnhancedArtifactDownloadReport(Artifact artifact) {
-        super(artifact);
-    }
-
-    public Throwable getFailure() {
-        return failure;
-    }
-
-    public void failed(Throwable throwable) {
-        setDownloadStatus(DownloadStatus.FAILED);
-        setDownloadDetails(throwable.getMessage());
-        failure = throwable;
-    }
-}
diff --git a/subprojects/core-impl/src/main/groovy/org/gradle/api/internal/artifacts/repositories/cachemanager/LocalFileRepositoryArtifactCache.java b/subprojects/core-impl/src/main/groovy/org/gradle/api/internal/artifacts/repositories/cachemanager/LocalFileRepositoryArtifactCache.java
new file mode 100644
index 0000000..f0f39ab
--- /dev/null
+++ b/subprojects/core-impl/src/main/groovy/org/gradle/api/internal/artifacts/repositories/cachemanager/LocalFileRepositoryArtifactCache.java
@@ -0,0 +1,43 @@
+/*
+ * Copyright 2013 the original author or authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY 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.cachemanager;
+
+import org.apache.ivy.core.module.id.ArtifactRevisionId;
+import org.gradle.api.internal.externalresource.DefaultLocallyAvailableExternalResource;
+import org.gradle.api.internal.externalresource.ExternalResource;
+import org.gradle.api.internal.externalresource.LocallyAvailableExternalResource;
+import org.gradle.internal.resource.local.DefaultLocallyAvailableResource;
+
+import java.io.File;
+import java.io.IOException;
+
+/**
+ * A cache manager for local repositories. Doesn't cache anything, and uses artifacts from their origin.
+ */
+public class LocalFileRepositoryArtifactCache implements RepositoryArtifactCache {
+
+    public boolean isLocal() {
+        return true;
+    }
+
+    public LocallyAvailableExternalResource downloadAndCacheArtifactFile(ArtifactRevisionId artifactId, ExternalResourceDownloader resourceDownloader, ExternalResource resource) throws IOException {
+        // Does not download, copy or cache local files.
+        assert resource.isLocal();
+        File file = new File(resource.getName());
+        assert file.isFile();
+        return new DefaultLocallyAvailableExternalResource(resource.getName(), new DefaultLocallyAvailableResource(file));
+    }
+}
diff --git a/subprojects/core-impl/src/main/groovy/org/gradle/api/internal/artifacts/repositories/cachemanager/LocalFileRepositoryCacheManager.java b/subprojects/core-impl/src/main/groovy/org/gradle/api/internal/artifacts/repositories/cachemanager/LocalFileRepositoryCacheManager.java
deleted file mode 100644
index 5ac65cd..0000000
--- a/subprojects/core-impl/src/main/groovy/org/gradle/api/internal/artifacts/repositories/cachemanager/LocalFileRepositoryCacheManager.java
+++ /dev/null
@@ -1,88 +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.api.internal.artifacts.repositories.cachemanager;
-
-import org.apache.ivy.core.cache.ArtifactOrigin;
-import org.apache.ivy.core.cache.CacheDownloadOptions;
-import org.apache.ivy.core.cache.CacheMetadataOptions;
-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.report.ArtifactDownloadReport;
-import org.apache.ivy.core.report.DownloadStatus;
-import org.apache.ivy.core.report.MetadataArtifactDownloadReport;
-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 extends AbstractRepositoryCacheManager {
-
-    public LocalFileRepositoryCacheManager(String name) {
-        super(name);
-    }
-
-    public EnhancedArtifactDownloadReport download(Artifact artifact, ArtifactResourceResolver resourceResolver, ResourceDownloader resourceDownloader, CacheDownloadOptions options) {
-        long start = System.currentTimeMillis();
-        EnhancedArtifactDownloadReport report = new EnhancedArtifactDownloadReport(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 resolvedResource, DependencyDescriptor dd, Artifact moduleArtifact, ResourceDownloader downloader, CacheMetadataOptions options) throws ParseException {
-        if (!moduleArtifact.isMetadata()) {
-            return null;
-        }
-
-        assert resolvedResource.getResource().isLocal();
-        File file = new File(resolvedResource.getResource().getName());
-        assert file.isFile();
-
-        ArtifactOrigin origin = new ArtifactOrigin(moduleArtifact, true, file.getAbsolutePath());
-        MetadataArtifactDownloadReport report = new MetadataArtifactDownloadReport(moduleArtifact);
-        report.setDownloadStatus(DownloadStatus.NO);
-        report.setArtifactOrigin(origin);
-        report.setSize(file.length());
-        report.setLocalFile(file);
-        report.setSearched(false);
-        report.setOriginalLocalFile(file);
-
-        ModuleDescriptor descriptor = parseModuleDescriptor(resolver, moduleArtifact, options, file, resolvedResource.getResource());
-        return new ResolvedModuleRevision(resolver, resolver, descriptor, report);
-    }
-}
diff --git a/subprojects/core-impl/src/main/groovy/org/gradle/api/internal/artifacts/repositories/cachemanager/RepositoryArtifactCache.java b/subprojects/core-impl/src/main/groovy/org/gradle/api/internal/artifacts/repositories/cachemanager/RepositoryArtifactCache.java
new file mode 100644
index 0000000..6be8069
--- /dev/null
+++ b/subprojects/core-impl/src/main/groovy/org/gradle/api/internal/artifacts/repositories/cachemanager/RepositoryArtifactCache.java
@@ -0,0 +1,45 @@
+/*
+ * Copyright 2012 the original author or authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY 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.cachemanager;
+
+import org.apache.ivy.core.module.id.ArtifactRevisionId;
+import org.gradle.api.internal.externalresource.ExternalResource;
+import org.gradle.api.internal.externalresource.LocallyAvailableExternalResource;
+
+import java.io.File;
+import java.io.IOException;
+
+/**
+ * This is a transitional interface for moving away from the Ivy RepositoryCacheManager.
+ */
+public interface RepositoryArtifactCache {
+    boolean isLocal();
+
+    /**
+     * Downloads the artifact file, moving it into the correct location in the cache.
+     *
+     * @param artifactId The id of the artifact this resource represents
+     * @param resourceDownloader An action to use for downloading the resource
+     * @param resource The artifact resource
+     * @return The cached resource
+     */
+    LocallyAvailableExternalResource downloadAndCacheArtifactFile(ArtifactRevisionId artifactId, ExternalResourceDownloader resourceDownloader, ExternalResource resource) throws IOException;
+
+    interface ExternalResourceDownloader {
+        public void download(ExternalResource resource, File dest) throws IOException;
+    }
+}
diff --git a/subprojects/core-impl/src/main/groovy/org/gradle/api/internal/artifacts/repositories/layout/MavenRepositoryLayout.java b/subprojects/core-impl/src/main/groovy/org/gradle/api/internal/artifacts/repositories/layout/MavenRepositoryLayout.java
index ae57ac4..97156fd 100644
--- a/subprojects/core-impl/src/main/groovy/org/gradle/api/internal/artifacts/repositories/layout/MavenRepositoryLayout.java
+++ b/subprojects/core-impl/src/main/groovy/org/gradle/api/internal/artifacts/repositories/layout/MavenRepositoryLayout.java
@@ -27,7 +27,7 @@ import java.net.URI;
  *     <li>Ivy: $baseUri/{@value IvyArtifactRepository#MAVEN_IVY_PATTERN}</li>
  * </ul>
  *
- * Following the maven convention, the 'organisation' value is further processed by replacing '.' with '/'.
+ * Following the Maven convention, the 'organisation' value is further processed by replacing '.' with '/'.
  * Note that the resolver will follow the layout only, but will <em>not</em> use .pom files for meta data. Ivy metadata files are required/published.
  */
 public class MavenRepositoryLayout extends RepositoryLayout {
diff --git a/subprojects/core-impl/src/main/groovy/org/gradle/api/internal/artifacts/repositories/legacy/AbstractRepositoryCacheManager.java b/subprojects/core-impl/src/main/groovy/org/gradle/api/internal/artifacts/repositories/legacy/AbstractRepositoryCacheManager.java
new file mode 100644
index 0000000..bbf1446
--- /dev/null
+++ b/subprojects/core-impl/src/main/groovy/org/gradle/api/internal/artifacts/repositories/legacy/AbstractRepositoryCacheManager.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.artifacts.repositories.legacy;
+
+import org.apache.ivy.core.cache.ArtifactOrigin;
+import org.apache.ivy.core.cache.CacheMetadataOptions;
+import org.apache.ivy.core.cache.ModuleDescriptorWriter;
+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.descriptor.ModuleDescriptor;
+import org.apache.ivy.core.module.id.ModuleRevisionId;
+import org.apache.ivy.core.resolve.ResolvedModuleRevision;
+import org.apache.ivy.core.settings.IvySettings;
+import org.apache.ivy.plugins.parser.ModuleDescriptorParser;
+import org.apache.ivy.plugins.parser.ModuleDescriptorParserRegistry;
+import org.apache.ivy.plugins.parser.ParserSettings;
+import org.apache.ivy.plugins.repository.Resource;
+import org.apache.ivy.plugins.resolver.DependencyResolver;
+import org.apache.ivy.plugins.resolver.util.ResolvedResource;
+import org.gradle.api.internal.artifacts.ivyservice.ivyresolve.IvyContextualiser;
+import org.gradle.internal.UncheckedException;
+
+import java.io.File;
+import java.io.IOException;
+import java.net.URL;
+import java.text.ParseException;
+
+abstract class AbstractRepositoryCacheManager implements RepositoryCacheManager {
+    protected final String name;
+
+    public AbstractRepositoryCacheManager(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 void originalToCachedModuleDescriptor(DependencyResolver resolver, ResolvedResource originalMetadataRef, Artifact requestedMetadataArtifact, ResolvedModuleRevision rmr, ModuleDescriptorWriter writer) {
+    }
+
+    public void clean() {
+    }
+
+    public void saveResolvedRevision(ModuleRevisionId dynamicMrid, String revision) {
+    }
+
+    protected ModuleDescriptor parseModuleDescriptor(DependencyResolver resolver, Artifact moduleArtifact, CacheMetadataOptions options, File artifactFile, Resource resource) throws ParseException {
+        ModuleRevisionId moduleRevisionId = moduleArtifact.getId().getModuleRevisionId();
+        try {
+            IvySettings ivySettings = IvyContextualiser.getIvyContext().getSettings();
+            ParserSettings parserSettings = new LegacyResolverParserSettings(ivySettings, resolver, moduleRevisionId);
+            ModuleDescriptorParser parser = ModuleDescriptorParserRegistry.getInstance().getParser(resource);
+            return parser.parseDescriptor(parserSettings, new URL(artifactFile.toURI().toASCIIString()), resource, options.isValidate());
+        } catch (IOException e) {
+            throw UncheckedException.throwAsUncheckedException(e);
+        }
+    }
+
+}
diff --git a/subprojects/core-impl/src/main/groovy/org/gradle/api/internal/artifacts/repositories/legacy/CustomIvyResolverRepositoryFactory.java b/subprojects/core-impl/src/main/groovy/org/gradle/api/internal/artifacts/repositories/legacy/CustomIvyResolverRepositoryFactory.java
new file mode 100644
index 0000000..e5b23c4
--- /dev/null
+++ b/subprojects/core-impl/src/main/groovy/org/gradle/api/internal/artifacts/repositories/legacy/CustomIvyResolverRepositoryFactory.java
@@ -0,0 +1,42 @@
+/*
+ * Copyright 2013 the original author or authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY 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.legacy;
+
+import org.apache.ivy.core.cache.RepositoryCacheManager;
+import org.apache.ivy.plugins.resolver.DependencyResolver;
+import org.gradle.api.artifacts.repositories.ArtifactRepository;
+import org.gradle.api.internal.artifacts.repositories.CustomResolverArtifactRepository;
+import org.gradle.logging.ProgressLoggerFactory;
+
+public class CustomIvyResolverRepositoryFactory implements LegacyDependencyResolverRepositoryFactory {
+    private final ProgressLoggerFactory progressLoggerFactory;
+    private final RepositoryCacheManager localCacheManager;
+    private final RepositoryCacheManager downloadingCacheManager;
+
+    public CustomIvyResolverRepositoryFactory(ProgressLoggerFactory progressLoggerFactory,
+                                              RepositoryCacheManager localCacheManager,
+                                              RepositoryCacheManager downloadingCacheManager) {
+        this.progressLoggerFactory = progressLoggerFactory;
+        this.localCacheManager = localCacheManager;
+        this.downloadingCacheManager = downloadingCacheManager;
+    }
+
+    public ArtifactRepository createRepository(DependencyResolver dependencyResolver) {
+        return new CustomResolverArtifactRepository(dependencyResolver, progressLoggerFactory, localCacheManager, downloadingCacheManager);
+    }
+
+}
diff --git a/subprojects/core-impl/src/main/groovy/org/gradle/api/internal/artifacts/repositories/legacy/DownloadingRepositoryCacheManager.java b/subprojects/core-impl/src/main/groovy/org/gradle/api/internal/artifacts/repositories/legacy/DownloadingRepositoryCacheManager.java
new file mode 100644
index 0000000..c5b90a4
--- /dev/null
+++ b/subprojects/core-impl/src/main/groovy/org/gradle/api/internal/artifacts/repositories/legacy/DownloadingRepositoryCacheManager.java
@@ -0,0 +1,155 @@
+/*
+ * Copyright 2011 the original author or authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY 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.legacy;
+
+import org.apache.ivy.core.cache.ArtifactOrigin;
+import org.apache.ivy.core.cache.CacheDownloadOptions;
+import org.apache.ivy.core.cache.CacheMetadataOptions;
+import org.apache.ivy.core.cache.DownloadListener;
+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.ArtifactRevisionId;
+import org.apache.ivy.core.report.ArtifactDownloadReport;
+import org.apache.ivy.core.report.DownloadStatus;
+import org.apache.ivy.core.report.MetadataArtifactDownloadReport;
+import org.apache.ivy.core.resolve.ResolvedModuleRevision;
+import org.apache.ivy.plugins.repository.ArtifactResourceResolver;
+import org.apache.ivy.plugins.repository.Resource;
+import org.apache.ivy.plugins.repository.ResourceDownloader;
+import org.apache.ivy.plugins.resolver.DependencyResolver;
+import org.apache.ivy.plugins.resolver.util.ResolvedResource;
+import org.gradle.api.internal.artifacts.ivyservice.CacheLockingManager;
+import org.gradle.api.internal.file.TemporaryFileProvider;
+import org.gradle.internal.Factory;
+import org.gradle.internal.filestore.FileStore;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+import java.io.File;
+import java.io.IOException;
+import java.text.ParseException;
+
+/**
+ * A cache manager for remote repositories, that downloads files and stores them in the FileStore provided.
+ */
+public class DownloadingRepositoryCacheManager extends AbstractRepositoryCacheManager {
+    private static final Logger LOGGER = LoggerFactory.getLogger(DownloadingRepositoryCacheManager.class);
+
+    private final FileStore<ArtifactRevisionId> fileStore;
+    private final TemporaryFileProvider temporaryFileProvider;
+    private final CacheLockingManager cacheLockingManager;
+
+    public DownloadingRepositoryCacheManager(String name, FileStore<ArtifactRevisionId> fileStore,
+                                             TemporaryFileProvider temporaryFileProvider, CacheLockingManager cacheLockingManager) {
+        super(name);
+        this.fileStore = fileStore;
+        this.temporaryFileProvider = temporaryFileProvider;
+        this.cacheLockingManager = cacheLockingManager;
+    }
+
+    public boolean isLocal() {
+        return false;
+    }
+
+    public EnhancedArtifactDownloadReport download(Artifact artifact, ArtifactResourceResolver resourceResolver,
+                                                   ResourceDownloader resourceDownloader, CacheDownloadOptions options) {
+        EnhancedArtifactDownloadReport adr = new EnhancedArtifactDownloadReport(artifact);
+
+        DownloadListener listener = options.getListener();
+        if (listener != null) {
+            listener.needArtifact(this, artifact);
+        }
+
+        long start = System.currentTimeMillis();
+        try {
+            ResolvedResource artifactRef = resourceResolver.resolve(artifact);
+            if (artifactRef != null) {
+                final Resource resource = artifactRef.getResource();
+                ArtifactOrigin origin = new ArtifactOrigin(artifact, resource.isLocal(), resource.getName());
+                if (listener != null) {
+                    listener.startArtifactDownload(this, artifactRef, artifact, origin);
+                }
+
+                File artifactFile = downloadAndCacheArtifactFile(artifact, resourceDownloader, artifactRef.getResource());
+
+                adr.setDownloadTimeMillis(System.currentTimeMillis() - start);
+                adr.setSize(artifactFile.length());
+                adr.setDownloadStatus(DownloadStatus.SUCCESSFUL);
+                adr.setArtifactOrigin(origin);
+                adr.setLocalFile(artifactFile);
+            } else {
+                adr.setDownloadTimeMillis(System.currentTimeMillis() - start);
+                adr.setDownloadStatus(DownloadStatus.FAILED);
+                adr.setDownloadDetails(ArtifactDownloadReport.MISSING_ARTIFACT);
+            }
+        } catch (Throwable throwable) {
+            adr.setDownloadTimeMillis(System.currentTimeMillis() - start);
+            adr.failed(throwable);
+        }
+        if (listener != null) {
+            listener.endArtifactDownload(this, artifact, adr, adr.getLocalFile());
+        }
+        return adr;
+    }
+
+    private File downloadAndCacheArtifactFile(final Artifact artifact, ResourceDownloader resourceDownloader, Resource resource) throws IOException {
+        final File tmpFile = temporaryFileProvider.createTemporaryFile("gradle_download", "bin");
+        try {
+            resourceDownloader.download(artifact, resource, tmpFile);
+            return cacheLockingManager.useCache(String.format("Store %s", artifact), new Factory<File>() {
+                public File create() {
+                    return fileStore.move(artifact.getId(), tmpFile).getFile();
+                }
+            });
+        } finally {
+            tmpFile.delete();
+        }
+    }
+
+    public ResolvedModuleRevision cacheModuleDescriptor(DependencyResolver resolver, final ResolvedResource resolvedResource, DependencyDescriptor dd, Artifact moduleArtifact, ResourceDownloader downloader, CacheMetadataOptions options) throws ParseException {
+        if (!moduleArtifact.isMetadata()) {
+            return null;
+        }
+
+        ArtifactResourceResolver artifactResourceResolver = new ArtifactResourceResolver() {
+            public ResolvedResource resolve(Artifact artifact) {
+                return resolvedResource;
+            }
+        };
+        ArtifactDownloadReport report = download(moduleArtifact, artifactResourceResolver, downloader, new CacheDownloadOptions().setListener(options.getListener()).setForce(true));
+
+        if (report.getDownloadStatus() == DownloadStatus.FAILED) {
+            LOGGER.warn("problem while downloading module descriptor: {}: {} ({} ms)", resolvedResource.getResource(), report.getDownloadDetails(), report.getDownloadTimeMillis());
+            return null;
+        }
+
+        ModuleDescriptor md = parseModuleDescriptor(resolver, moduleArtifact, options, report.getLocalFile(), resolvedResource.getResource());
+        LOGGER.debug("\t{}: parsed downloaded md file for {}; parsed={}" + getName(), moduleArtifact.getModuleRevisionId(), md.getModuleRevisionId());
+
+        MetadataArtifactDownloadReport madr = new MetadataArtifactDownloadReport(md.getMetadataArtifact());
+        madr.setSearched(true);
+        madr.setDownloadStatus(report.getDownloadStatus());
+        madr.setDownloadDetails(report.getDownloadDetails());
+        madr.setArtifactOrigin(report.getArtifactOrigin());
+        madr.setDownloadTimeMillis(report.getDownloadTimeMillis());
+        madr.setOriginalLocalFile(report.getLocalFile());
+        madr.setSize(report.getSize());
+
+        return new ResolvedModuleRevision(resolver, resolver, md, madr);
+    }
+
+}
diff --git a/subprojects/core-impl/src/main/groovy/org/gradle/api/internal/artifacts/repositories/legacy/EnhancedArtifactDownloadReport.java b/subprojects/core-impl/src/main/groovy/org/gradle/api/internal/artifacts/repositories/legacy/EnhancedArtifactDownloadReport.java
new file mode 100644
index 0000000..50ff9b9
--- /dev/null
+++ b/subprojects/core-impl/src/main/groovy/org/gradle/api/internal/artifacts/repositories/legacy/EnhancedArtifactDownloadReport.java
@@ -0,0 +1,38 @@
+/*
+ * Copyright 2012 the original author or authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY 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.legacy;
+
+import org.apache.ivy.core.module.descriptor.Artifact;
+import org.apache.ivy.core.report.ArtifactDownloadReport;
+import org.apache.ivy.core.report.DownloadStatus;
+
+public class EnhancedArtifactDownloadReport extends ArtifactDownloadReport {
+    private Throwable failure;
+
+    public EnhancedArtifactDownloadReport(Artifact artifact) {
+        super(artifact);
+    }
+
+    public Throwable getFailure() {
+        return failure;
+    }
+
+    public void failed(Throwable throwable) {
+        setDownloadStatus(DownloadStatus.FAILED);
+        setDownloadDetails(throwable.getMessage());
+        failure = throwable;
+    }
+}
diff --git a/subprojects/core-impl/src/main/groovy/org/gradle/api/internal/artifacts/repositories/legacy/LegacyDependencyResolverRepositoryFactory.java b/subprojects/core-impl/src/main/groovy/org/gradle/api/internal/artifacts/repositories/legacy/LegacyDependencyResolverRepositoryFactory.java
new file mode 100644
index 0000000..9a27910
--- /dev/null
+++ b/subprojects/core-impl/src/main/groovy/org/gradle/api/internal/artifacts/repositories/legacy/LegacyDependencyResolverRepositoryFactory.java
@@ -0,0 +1,24 @@
+/*
+ * Copyright 2013 the original author or authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY 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.legacy;
+
+import org.apache.ivy.plugins.resolver.DependencyResolver;
+import org.gradle.api.artifacts.repositories.ArtifactRepository;
+
+public interface LegacyDependencyResolverRepositoryFactory {
+    ArtifactRepository createRepository(DependencyResolver dependencyResolver);
+}
diff --git a/subprojects/core-impl/src/main/groovy/org/gradle/api/internal/artifacts/repositories/legacy/LegacyResolverParserSettings.java b/subprojects/core-impl/src/main/groovy/org/gradle/api/internal/artifacts/repositories/legacy/LegacyResolverParserSettings.java
new file mode 100644
index 0000000..8030383
--- /dev/null
+++ b/subprojects/core-impl/src/main/groovy/org/gradle/api/internal/artifacts/repositories/legacy/LegacyResolverParserSettings.java
@@ -0,0 +1,98 @@
+/*
+ * Copyright 2011 the original author or authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY 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.legacy;
+
+import org.apache.ivy.core.RelativeUrlResolver;
+import org.apache.ivy.core.cache.ResolutionCacheManager;
+import org.apache.ivy.core.module.id.ModuleId;
+import org.apache.ivy.core.module.id.ModuleRevisionId;
+import org.apache.ivy.core.module.status.StatusManager;
+import org.apache.ivy.plugins.conflict.ConflictManager;
+import org.apache.ivy.plugins.matcher.PatternMatcher;
+import org.apache.ivy.plugins.namespace.Namespace;
+import org.apache.ivy.plugins.parser.ParserSettings;
+import org.apache.ivy.plugins.resolver.DependencyResolver;
+
+import java.io.File;
+import java.util.Map;
+
+/**
+ * ParserSettings that control the scope of searches carried out during parsing.
+ * If the parser asks for a resolver for the currently resolving revision, the resolver scope is only the repository where the module was resolved.
+ * If the parser asks for a resolver for a different revision, the resolver scope is all repositories.
+ */
+public class LegacyResolverParserSettings implements ParserSettings {
+    private final ParserSettings settings;
+    private final DependencyResolver currentResolver;
+    private final ModuleRevisionId currentRevisionId;
+
+    public LegacyResolverParserSettings(ParserSettings settings, DependencyResolver currentResolver, ModuleRevisionId currentRevisionId) {
+        this.settings = settings;
+        this.currentResolver = currentResolver;
+        this.currentRevisionId = currentRevisionId;
+    }
+
+    public DependencyResolver getResolver(ModuleRevisionId mRevId) {
+        if (mRevId.equals(currentRevisionId)) {
+            return currentResolver;
+        }
+        return settings.getResolver(mRevId);
+    }
+
+    public ConflictManager getConflictManager(String name) {
+        return settings.getConflictManager(name);
+    }
+
+    public String substitute(String value) {
+        return settings.substitute(value);
+    }
+
+    public Map substitute(Map strings) {
+        return settings.substitute(strings);
+    }
+
+    public ResolutionCacheManager getResolutionCacheManager() {
+        return settings.getResolutionCacheManager();
+    }
+
+    public PatternMatcher getMatcher(String matcherName) {
+        return settings.getMatcher(matcherName);
+    }
+
+    public Namespace getNamespace(String namespace) {
+        return settings.getNamespace(namespace);
+    }
+
+    public StatusManager getStatusManager() {
+        return settings.getStatusManager();
+    }
+
+    public RelativeUrlResolver getRelativeUrlResolver() {
+        return settings.getRelativeUrlResolver();
+    }
+
+    public File resolveFile(String filename) {
+        return settings.resolveFile(filename);
+    }
+
+    public String getDefaultBranch(ModuleId moduleId) {
+        return settings.getDefaultBranch(moduleId);
+    }
+
+    public Namespace getContextNamespace() {
+        return settings.getContextNamespace();
+    }
+}
diff --git a/subprojects/core-impl/src/main/groovy/org/gradle/api/internal/artifacts/repositories/legacy/LocalFileRepositoryCacheManager.java b/subprojects/core-impl/src/main/groovy/org/gradle/api/internal/artifacts/repositories/legacy/LocalFileRepositoryCacheManager.java
new file mode 100644
index 0000000..2e1520c
--- /dev/null
+++ b/subprojects/core-impl/src/main/groovy/org/gradle/api/internal/artifacts/repositories/legacy/LocalFileRepositoryCacheManager.java
@@ -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.api.internal.artifacts.repositories.legacy;
+
+import org.apache.ivy.core.cache.ArtifactOrigin;
+import org.apache.ivy.core.cache.CacheDownloadOptions;
+import org.apache.ivy.core.cache.CacheMetadataOptions;
+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.report.ArtifactDownloadReport;
+import org.apache.ivy.core.report.DownloadStatus;
+import org.apache.ivy.core.report.MetadataArtifactDownloadReport;
+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 extends AbstractRepositoryCacheManager {
+
+    public LocalFileRepositoryCacheManager(String name) {
+        super(name);
+    }
+
+    public EnhancedArtifactDownloadReport download(Artifact artifact, ArtifactResourceResolver resourceResolver, ResourceDownloader resourceDownloader, CacheDownloadOptions options) {
+        ResolvedResource resolvedResource = resourceResolver.resolve(artifact);
+        long start = System.currentTimeMillis();
+        EnhancedArtifactDownloadReport report = new EnhancedArtifactDownloadReport(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 resolvedResource, DependencyDescriptor dd, Artifact moduleArtifact, ResourceDownloader downloader, CacheMetadataOptions options) throws ParseException {
+        if (!moduleArtifact.isMetadata()) {
+            return null;
+        }
+
+        assert resolvedResource.getResource().isLocal();
+        File file = new File(resolvedResource.getResource().getName());
+        assert file.isFile();
+
+        ArtifactOrigin origin = new ArtifactOrigin(moduleArtifact, true, file.getAbsolutePath());
+        MetadataArtifactDownloadReport report = new MetadataArtifactDownloadReport(moduleArtifact);
+        report.setDownloadStatus(DownloadStatus.NO);
+        report.setArtifactOrigin(origin);
+        report.setSize(file.length());
+        report.setLocalFile(file);
+        report.setSearched(false);
+        report.setOriginalLocalFile(file);
+
+        ModuleDescriptor descriptor = parseModuleDescriptor(resolver, moduleArtifact, options, file, resolvedResource.getResource());
+        return new ResolvedModuleRevision(resolver, resolver, descriptor, report);
+    }
+}
diff --git a/subprojects/core-impl/src/main/groovy/org/gradle/api/internal/artifacts/repositories/resolver/AbstractVersionList.java b/subprojects/core-impl/src/main/groovy/org/gradle/api/internal/artifacts/repositories/resolver/AbstractVersionList.java
index 695ecbe..f423aae 100644
--- a/subprojects/core-impl/src/main/groovy/org/gradle/api/internal/artifacts/repositories/resolver/AbstractVersionList.java
+++ b/subprojects/core-impl/src/main/groovy/org/gradle/api/internal/artifacts/repositories/resolver/AbstractVersionList.java
@@ -16,48 +16,22 @@
 
 package org.gradle.api.internal.artifacts.repositories.resolver;
 
-import org.apache.ivy.plugins.latest.ArtifactInfo;
-import org.apache.ivy.plugins.latest.LatestStrategy;
+import com.google.common.collect.Lists;
+import org.gradle.api.internal.artifacts.ivyservice.ivyresolve.strategy.LatestStrategy;
 
-import java.util.ArrayList;
 import java.util.Collections;
 import java.util.List;
 
 abstract class AbstractVersionList implements VersionList {
     public boolean isEmpty() {
-        return getVersionStrings().isEmpty();
+        return getVersions().isEmpty();
     }
 
-    public List<String> sortLatestFirst(LatestStrategy latestStrategy) {
-        List<String> versions = new ArrayList<String>(getVersionStrings());
-        ArtifactInfo[] artifactInfos = new ArtifactInfo[versions.size()];
-        for (int i = 0; i < versions.size(); i++) {
-            String version = versions.get(i);
-            artifactInfos[i] = new VersionArtifactInfo(version);
-        }
-        List<ArtifactInfo> sorted = latestStrategy.sort(artifactInfos);
+    public List<ListedVersion> sortLatestFirst(LatestStrategy latestStrategy) {
+        List<ListedVersion> versions = Lists.newArrayList(getVersions());
+        List<ListedVersion> sorted = latestStrategy.sort(versions);
         Collections.reverse(sorted);
 
-        List<String> sortedVersions = new ArrayList<String>();
-        for (ArtifactInfo info : sorted) {
-            sortedVersions.add(info.getRevision());
-        }
-        return sortedVersions;
-    }
-
-    private class VersionArtifactInfo implements ArtifactInfo {
-        private final String version;
-
-        private VersionArtifactInfo(String version) {
-            this.version = version;
-        }
-
-        public String getRevision() {
-            return version;
-        }
-
-        public long getLastModified() {
-            return 0;
-        }
+        return sorted;
     }
 }
diff --git a/subprojects/core-impl/src/main/groovy/org/gradle/api/internal/artifacts/repositories/resolver/ChainedVersionLister.java b/subprojects/core-impl/src/main/groovy/org/gradle/api/internal/artifacts/repositories/resolver/ChainedVersionLister.java
index fb1657c..bc4ec85 100644
--- a/subprojects/core-impl/src/main/groovy/org/gradle/api/internal/artifacts/repositories/resolver/ChainedVersionLister.java
+++ b/subprojects/core-impl/src/main/groovy/org/gradle/api/internal/artifacts/repositories/resolver/ChainedVersionLister.java
@@ -17,7 +17,7 @@
 package org.gradle.api.internal.artifacts.repositories.resolver;
 
 import org.apache.ivy.core.module.descriptor.Artifact;
-import org.apache.ivy.core.module.id.ModuleRevisionId;
+import org.gradle.api.artifacts.ModuleVersionSelector;
 import org.gradle.api.internal.resource.ResourceException;
 import org.gradle.api.internal.resource.ResourceNotFoundException;
 import org.gradle.util.DeprecationLogger;
@@ -35,10 +35,10 @@ public class ChainedVersionLister implements VersionLister {
         this.versionListers = Arrays.asList(versionlisters);
     }
 
-    public VersionList getVersionList(final ModuleRevisionId moduleRevisionId)  {
+    public VersionList getVersionList(final ModuleVersionSelector selector)  {
         final List<VersionList> versionLists = new ArrayList<VersionList>();
         for (VersionLister lister : versionListers) {
-            versionLists.add(lister.getVersionList(moduleRevisionId));
+            versionLists.add(lister.getVersionList(selector));
         }
         return new AbstractVersionList() {
             public void visit(ResourcePattern pattern, Artifact artifact) throws ResourceNotFoundException, ResourceException {
@@ -56,21 +56,21 @@ public class ChainedVersionLister implements VersionLister {
                         if (versionListIterator.hasNext()) {
                             String deprecationMessage = String.format(
                                     "Error listing versions of %s using %s. Will attempt an alternate way to list versions",
-                                    moduleRevisionId, list.getClass()
+                                    selector, list.getClass()
                             );
                             DeprecationLogger.nagUserOfDeprecatedBehaviour(deprecationMessage);
                             LOGGER.debug(deprecationMessage, e);
                         } else {
-                            throw new ResourceException(String.format("Failed to list versions for %s.", moduleRevisionId), e);
+                            throw new ResourceException(String.format("Failed to list versions for %s.", selector), e);
                         }
                     }
                 }
             }
 
-            public Set<String> getVersionStrings() {
-                Set<String> allVersions = new HashSet<String>();
+            public Set<ListedVersion> getVersions() {
+                Set<ListedVersion> allVersions = new HashSet<ListedVersion>();
                 for (VersionList versionList : versionLists) {
-                    allVersions.addAll(versionList.getVersionStrings());
+                    allVersions.addAll(versionList.getVersions());
                 }
                 return allVersions;
             }
diff --git a/subprojects/core-impl/src/main/groovy/org/gradle/api/internal/artifacts/repositories/resolver/ComponentMetadataDetailsAdapter.java b/subprojects/core-impl/src/main/groovy/org/gradle/api/internal/artifacts/repositories/resolver/ComponentMetadataDetailsAdapter.java
new file mode 100644
index 0000000..fa65350
--- /dev/null
+++ b/subprojects/core-impl/src/main/groovy/org/gradle/api/internal/artifacts/repositories/resolver/ComponentMetadataDetailsAdapter.java
@@ -0,0 +1,58 @@
+/*
+ * Copyright 2013 the original author or authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY 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.resolver;
+
+import org.gradle.api.artifacts.ComponentMetadataDetails;
+import org.gradle.api.artifacts.ModuleVersionIdentifier;
+import org.gradle.api.internal.artifacts.ivyservice.ivyresolve.MutableModuleVersionMetaData;
+
+import java.util.List;
+
+public class ComponentMetadataDetailsAdapter implements ComponentMetadataDetails {
+    private final MutableModuleVersionMetaData metadata;
+
+    public ComponentMetadataDetailsAdapter(MutableModuleVersionMetaData metadata) {
+        this.metadata = metadata;
+    }
+
+    public ModuleVersionIdentifier getId() {
+        return metadata.getId();
+    }
+
+    public boolean isChanging() {
+        return metadata.isChanging();
+    }
+
+    public String getStatus() {
+        return metadata.getStatus();
+    }
+
+    public List<String> getStatusScheme() {
+        return metadata.getStatusScheme();
+    }
+
+    public void setChanging(boolean changing) {
+        metadata.setChanging(changing);
+    }
+
+    public void setStatus(String status) {
+        metadata.setStatus(status);
+    }
+
+    public void setStatusScheme(List<String> statusScheme) {
+        metadata.setStatusScheme(statusScheme);
+    }
+}
diff --git a/subprojects/core-impl/src/main/groovy/org/gradle/api/internal/artifacts/repositories/resolver/DefaultVersionList.java b/subprojects/core-impl/src/main/groovy/org/gradle/api/internal/artifacts/repositories/resolver/DefaultVersionList.java
index 9eafb22..fdec609 100644
--- a/subprojects/core-impl/src/main/groovy/org/gradle/api/internal/artifacts/repositories/resolver/DefaultVersionList.java
+++ b/subprojects/core-impl/src/main/groovy/org/gradle/api/internal/artifacts/repositories/resolver/DefaultVersionList.java
@@ -16,34 +16,33 @@
 
 package org.gradle.api.internal.artifacts.repositories.resolver;
 
-import com.google.common.collect.Iterables;
 import org.apache.ivy.core.module.descriptor.Artifact;
 import org.gradle.api.internal.resource.ResourceException;
 import org.gradle.api.internal.resource.ResourceNotFoundException;
 
+import java.util.HashMap;
 import java.util.HashSet;
-import java.util.List;
+import java.util.Map;
 import java.util.Set;
 
 public class DefaultVersionList extends AbstractVersionList {
-    private final Set<String> versions = new HashSet<String>();
-
-    public DefaultVersionList(List<String> versionStrings) {
-        this.versions.addAll(versionStrings);
-    }
+    private final Map<String, ListedVersion> versions = new HashMap<String, ListedVersion>();
 
     public DefaultVersionList() {
     }
 
-    protected void add(Iterable<String> versionStrings) {
-        Iterables.addAll(versions, versionStrings);
+    protected void add(ListedVersion newVersion) {
+        if (versions.containsKey(newVersion.getVersion())) {
+            return;
+        }
+        versions.put(newVersion.getVersion(), newVersion);
     }
 
     public void visit(ResourcePattern pattern, Artifact artifact) throws ResourceNotFoundException, ResourceException {
         throw new UnsupportedOperationException();
     }
 
-    public Set<String> getVersionStrings() {
-        return versions;
+    public Set<ListedVersion> getVersions() {
+        return new HashSet<ListedVersion>(versions.values());
     }
 }
diff --git a/subprojects/core-impl/src/main/groovy/org/gradle/api/internal/artifacts/repositories/resolver/ExternalResourceResolver.java b/subprojects/core-impl/src/main/groovy/org/gradle/api/internal/artifacts/repositories/resolver/ExternalResourceResolver.java
index 3dfdda8..f54fa91 100644
--- a/subprojects/core-impl/src/main/groovy/org/gradle/api/internal/artifacts/repositories/resolver/ExternalResourceResolver.java
+++ b/subprojects/core-impl/src/main/groovy/org/gradle/api/internal/artifacts/repositories/resolver/ExternalResourceResolver.java
@@ -16,48 +16,31 @@
 
 package org.gradle.api.internal.artifacts.repositories.resolver;
 
+import com.google.common.base.Joiner;
 import org.apache.ivy.core.cache.ArtifactOrigin;
-import org.apache.ivy.core.cache.CacheDownloadOptions;
-import org.apache.ivy.core.cache.CacheMetadataOptions;
-import org.apache.ivy.core.cache.RepositoryCacheManager;
 import org.apache.ivy.core.module.descriptor.*;
 import org.apache.ivy.core.module.id.ArtifactRevisionId;
-import org.apache.ivy.core.module.id.ModuleId;
 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.report.DownloadStatus;
-import org.apache.ivy.core.report.MetadataArtifactDownloadReport;
-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.latest.LatestStrategy;
+import org.apache.ivy.core.settings.IvySettings;
 import org.apache.ivy.plugins.matcher.PatternMatcher;
-import org.apache.ivy.plugins.namespace.Namespace;
-import org.apache.ivy.plugins.parser.ModuleDescriptorParser;
-import org.apache.ivy.plugins.parser.ModuleDescriptorParserRegistry;
-import org.apache.ivy.plugins.repository.ArtifactResourceResolver;
-import org.apache.ivy.plugins.repository.Resource;
-import org.apache.ivy.plugins.repository.ResourceDownloader;
-import org.apache.ivy.plugins.resolver.BasicResolver;
-import org.apache.ivy.plugins.resolver.DependencyResolver;
-import org.apache.ivy.plugins.resolver.ResolverSettings;
-import org.apache.ivy.plugins.resolver.util.MDResolvedResource;
-import org.apache.ivy.plugins.resolver.util.ResolvedResource;
-import org.apache.ivy.plugins.resolver.util.ResourceMDParser;
-import org.apache.ivy.plugins.version.VersionMatcher;
 import org.apache.ivy.util.ChecksumHelper;
-import org.apache.ivy.util.Message;
+import org.gradle.api.artifacts.ArtifactIdentifier;
+import org.gradle.api.artifacts.ModuleVersionSelector;
+import org.gradle.api.internal.artifacts.*;
 import org.gradle.api.internal.artifacts.ivyservice.BuildableArtifactResolveResult;
+import org.gradle.api.internal.artifacts.ivyservice.DependencyToModuleVersionResolver;
+import org.gradle.api.internal.artifacts.ivyservice.IvyUtil;
 import org.gradle.api.internal.artifacts.ivyservice.ModuleVersionResolveException;
-import org.gradle.api.internal.artifacts.ivyservice.ivyresolve.ArtifactResolveException;
-import org.gradle.api.internal.artifacts.ivyservice.ivyresolve.BuildableModuleVersionMetaData;
-import org.gradle.api.internal.artifacts.ivyservice.ivyresolve.ModuleSource;
-import org.gradle.api.internal.artifacts.repositories.cachemanager.EnhancedArtifactDownloadReport;
+import org.gradle.api.internal.artifacts.ivyservice.ivyresolve.*;
+import org.gradle.api.internal.artifacts.ivyservice.ivyresolve.parser.MetaDataParseException;
+import org.gradle.api.internal.artifacts.ivyservice.ivyresolve.parser.MetaDataParser;
+import org.gradle.api.internal.artifacts.ivyservice.ivyresolve.parser.ModuleDescriptorAdapter;
+import org.gradle.api.internal.artifacts.ivyservice.ivyresolve.strategy.LatestStrategy;
+import org.gradle.api.internal.artifacts.ivyservice.ivyresolve.strategy.ResolverStrategy;
+import org.gradle.api.internal.artifacts.ivyservice.ivyresolve.strategy.VersionMatcher;
+import org.gradle.api.internal.artifacts.repositories.cachemanager.RepositoryArtifactCache;
 import org.gradle.api.internal.externalresource.ExternalResource;
+import org.gradle.api.internal.externalresource.LocallyAvailableExternalResource;
 import org.gradle.api.internal.externalresource.MetaDataOnlyExternalResource;
 import org.gradle.api.internal.externalresource.MissingExternalResource;
 import org.gradle.api.internal.externalresource.local.LocallyAvailableResourceCandidates;
@@ -65,58 +48,72 @@ import org.gradle.api.internal.externalresource.local.LocallyAvailableResourceFi
 import org.gradle.api.internal.externalresource.metadata.ExternalResourceMetaData;
 import org.gradle.api.internal.externalresource.transport.ExternalResourceRepository;
 import org.gradle.api.internal.resource.ResourceNotFoundException;
+import org.gradle.internal.SystemProperties;
 import org.gradle.util.GFileUtils;
 import org.slf4j.Logger;
 import org.slf4j.LoggerFactory;
 
 import java.io.File;
 import java.io.IOException;
-import java.text.ParseException;
-import java.util.*;
+import java.util.ArrayList;
+import java.util.Collections;
+import java.util.List;
+import java.util.Map;
 
-public class ExternalResourceResolver implements DependencyResolver {
+import static org.gradle.api.internal.artifacts.repositories.cachemanager.RepositoryArtifactCache.ExternalResourceDownloader;
+
+public class ExternalResourceResolver implements ModuleVersionPublisher, ConfiguredModuleVersionRepository {
     private static final Logger LOGGER = LoggerFactory.getLogger(ExternalResourceResolver.class);
 
+    private final MetaDataParser metaDataParser;
+    private final ModuleMetadataProcessor metadataProcessor;
+
     private List<String> ivyPatterns = new ArrayList<String>();
     private List<String> artifactPatterns = new ArrayList<String>();
-    private boolean m2compatible;
-    private boolean checkconsistency = true;
-    private boolean allownomd = true;
+    private boolean m2Compatible;
+    private boolean checkConsistency = true;
+    private boolean allowMissingDescriptor = true;
     private boolean force;
     private String checksums;
     private String name;
-    private ResolverSettings settings;
-    private LatestStrategy latestStrategy;
-    private String latestStrategyName;
-    private String cacheManagerName;
-    private RepositoryCacheManager repositoryCacheManager;
+    private RepositoryArtifactCache repositoryCacheManager;
     private String changingMatcherName;
     private String changingPattern;
-    private Boolean checkmodified;
+    private DependencyToModuleVersionResolver nestedResolver;
 
     private final ExternalResourceRepository repository;
     private final LocallyAvailableResourceFinder<ArtifactRevisionId> locallyAvailableResourceFinder;
+    private final LatestStrategy latestStrategy;
+    private final VersionMatcher versionMatcher;
+
     protected VersionLister versionLister;
-    private ArtifactResourceResolver artifactResourceResolver = new ArtifactResourceResolver() {
-        public ResolvedResource resolve(Artifact artifact) {
-            return getArtifactRef(artifact, null);
-        }
-    };
-    private final ResourceDownloader resourceDownloader = new ResourceDownloader() {
-        public void download(Artifact artifact, Resource resource, File dest) throws IOException {
-            getAndCheck(resource, dest);
+
+    // TODO:DAZ Get rid of this
+    private final ExternalResourceDownloader resourceDownloader = new ExternalResourceDownloader() {
+        public void download(ExternalResource resource, File dest) throws IOException {
+            get(resource, dest);
+            verifyChecksums(resource, dest);
         }
     };
 
     public ExternalResourceResolver(String name,
                                     ExternalResourceRepository repository,
                                     VersionLister versionLister,
-                                    LocallyAvailableResourceFinder<ArtifactRevisionId> locallyAvailableResourceFinder
-    ) {
+                                    LocallyAvailableResourceFinder<ArtifactRevisionId> locallyAvailableResourceFinder,
+                                    MetaDataParser metaDataParser, ModuleMetadataProcessor metadataProcessor) {
         this.name = name;
         this.versionLister = versionLister;
         this.repository = repository;
         this.locallyAvailableResourceFinder = locallyAvailableResourceFinder;
+        this.metaDataParser = metaDataParser;
+        this.metadataProcessor = metadataProcessor;
+
+        latestStrategy = ResolverStrategy.INSTANCE.getLatestStrategy();
+        versionMatcher = ResolverStrategy.INSTANCE.getVersionMatcher();
+    }
+
+    public String getId() {
+        return DependencyResolverIdentifier.forExternalResourceResolver(this);
     }
 
     public String getName() {
@@ -127,367 +124,267 @@ public class ExternalResourceResolver implements DependencyResolver {
         this.name = name;
     }
 
-    public String toString() {
-        return getName();
+    public boolean isDynamicResolveMode() {
+        return false;
     }
 
-    public void setSettings(ResolverSettings ivy) {
-        this.settings = ivy;
+    public String toString() {
+        return String.format("Repository '%s'", getName());
     }
 
-    public ResolverSettings getSettings() {
-        return settings;
+    public void setResolver(DependencyToModuleVersionResolver resolver) {
+        this.nestedResolver = resolver;
     }
 
     protected ExternalResourceRepository getRepository() {
         return repository;
     }
 
-    public ResolvedModuleRevision getDependency(DependencyDescriptor dd, ResolveData data) throws ParseException {
-        // This is not used
-        throw new UnsupportedOperationException();
+    public boolean isLocal() {
+        return repositoryCacheManager.isLocal();
     }
 
-    public void getDependency(DependencyDescriptor dependencyDescriptor, BuildableModuleVersionMetaData result) {
-        ModuleRevisionId nsMrid = dependencyDescriptor.getDependencyRevisionId();
+    public void getDependency(DependencyMetaData dependency, BuildableModuleVersionMetaDataResolveResult result) {
+        getDependency(dependency.getDescriptor(), result);
+    }
 
-        boolean isDynamic = getVersionMatcher().isDynamic(nsMrid);
+    protected void getDependency(DependencyDescriptor dependencyDescriptor, BuildableModuleVersionMetaDataResolveResult result) {
+        ModuleRevisionId moduleRevisionId = dependencyDescriptor.getDependencyRevisionId();
+        boolean isDynamic = versionMatcher.isDynamic(moduleRevisionId.getRevision());
 
-        ResolvedResource ivyRef = findIvyFileRef(dependencyDescriptor);
+        ResolvedArtifact ivyRef = findIvyFileRef(dependencyDescriptor);
 
         // get module descriptor
-        ModuleDescriptor nsMd;
         if (ivyRef == null) {
             if (!isAllownomd()) {
-                LOGGER.debug("No ivy file found for module '{}' in repository '{}'.", nsMrid, getName());
+                LOGGER.debug("No ivy file found for module '{}' in repository '{}'.", moduleRevisionId, getName());
                 result.missing();
                 return;
             }
-            nsMd = DefaultModuleDescriptor.newDefaultInstance(nsMrid, dependencyDescriptor.getAllDependencyArtifacts());
-            ResolvedResource artifactRef = findFirstArtifactRef(nsMd);
+            DefaultModuleDescriptor generatedModuleDescriptor = DefaultModuleDescriptor.newDefaultInstance(moduleRevisionId, dependencyDescriptor.getAllDependencyArtifacts());
+            ResolvedArtifact artifactRef = findAnyArtifact(generatedModuleDescriptor);
             if (artifactRef == null) {
-                LOGGER.debug("No ivy file nor artifact found for module '{}' in repository '{}'.", nsMrid, getName());
+                LOGGER.debug("No ivy file nor artifact found for module '{}' in repository '{}'.", moduleRevisionId, getName());
                 result.missing();
             } else {
-                long lastModified = artifactRef.getLastModified();
-                if (lastModified != 0 && nsMd instanceof DefaultModuleDescriptor) {
-                    ((DefaultModuleDescriptor) nsMd).setLastModified(lastModified);
+                long lastModified = artifactRef.resource.getLastModified();
+                if (lastModified != 0) {
+                    generatedModuleDescriptor.setLastModified(lastModified);
                 }
-                LOGGER.debug("No ivy file found for module '{}' in repository '{}', using default data instead.", nsMrid, getName());
+                LOGGER.debug("No ivy file found for module '{}' in repository '{}', using default data instead.", moduleRevisionId, getName());
                 if (isDynamic) {
-                    nsMd.setResolvedModuleRevisionId(ModuleRevisionId.newInstance(nsMrid, artifactRef.getRevision()));
+                    generatedModuleDescriptor.setResolvedModuleRevisionId(artifactRef.artifact.getModuleRevisionId());
                 }
-                result.resolved(nsMd, isChanging(nsMd), null);
+                result.resolved(generatedModuleDescriptor, isChanging(generatedModuleDescriptor), null);
             }
         } else {
             try {
-                ResolvedModuleRevision rmr = null;
-                if (ivyRef instanceof MDResolvedResource) {
-                    rmr = ((MDResolvedResource) ivyRef).getResolvedModuleRevision();
-                }
-                if (rmr == null) {
-                    rmr = parse(ivyRef, dependencyDescriptor);
+                ModuleVersionMetaData moduleVersionMetaData;
+                if (ivyRef instanceof DownloadedAndParsedMetaDataArtifact) {
+                    moduleVersionMetaData = ((DownloadedAndParsedMetaDataArtifact) ivyRef).moduleVersionMetaData;
+                } else {
+                    moduleVersionMetaData = getArtifactMetadata(ivyRef.artifact, ivyRef.resource);
                 }
 
-                nsMd = rmr.getDescriptor();
-
-                // check descriptor data is in sync with resource revision and names
                 if (isCheckconsistency()) {
-                    checkDescriptorConsistency(nsMrid, nsMd, ivyRef);
+                    checkMetadataConsistency(DefaultModuleVersionSelector.newSelector(moduleRevisionId), moduleVersionMetaData, ivyRef);
                 }
-                LOGGER.debug("Ivy file found for module '{}' in repository '{}'.", nsMrid, getName());
-                result.resolved(nsMd, isChanging(nsMd), null);
-            } catch (ParseException e) {
-                result.failed(new ModuleVersionResolveException(nsMrid, e));
+                LOGGER.debug("Ivy file found for module '{}' in repository '{}'.", moduleRevisionId, getName());
+                result.resolved(moduleVersionMetaData.getDescriptor(), isChanging(moduleVersionMetaData.getDescriptor()), null);
+            } catch (MetaDataParseException e) {
+                result.failed(new ModuleVersionResolveException(moduleRevisionId, e));
             }
         }
     }
 
-    protected VersionMatcher getVersionMatcher() {
-        return getSettings().getVersionMatcher();
+    private MutableModuleVersionMetaData getArtifactMetadata(Artifact artifact, ExternalResource resource) {
+        MutableModuleVersionMetaData metadata = doGetArtifactMetadata(artifact, resource);
+        metadataProcessor.process(metadata);
+        return metadata;
     }
 
-    private ResolvedModuleRevision parse(final ResolvedResource mdRef, DependencyDescriptor dd) throws ParseException {
-        //TODO: check why we don't use our own ParserRegistry here.
-        ModuleRevisionId mrid = dd.getDependencyRevisionId();
-        ModuleDescriptorParser parser = ModuleDescriptorParserRegistry.getInstance().getParser(mdRef.getResource());
-        if (parser == null) {
-            throw new RuntimeException("no module descriptor parser available for " + mdRef.getResource());
-        }
-
-        ModuleRevisionId resolvedMrid = mrid;
-
-        // first check if this dependency has not yet been resolved
-        if (getVersionMatcher().isDynamic(mrid)) {
-            resolvedMrid = ModuleRevisionId.newInstance(mrid, mdRef.getRevision());
-        }
-
-        Artifact moduleArtifact = parser.getMetadataArtifact(resolvedMrid, mdRef.getResource());
-        return getRepositoryCacheManager().cacheModuleDescriptor(this, mdRef, dd, moduleArtifact, resourceDownloader, new CacheMetadataOptions());
-    }
-
-    private void checkDescriptorConsistency(ModuleRevisionId mrid, ModuleDescriptor md,
-                                            ResolvedResource ivyRef) throws ParseException {
-        boolean ok = true;
-        StringBuilder errors = new StringBuilder();
-        if (!mrid.getOrganisation().equals(md.getModuleRevisionId().getOrganisation())) {
-            Message.error("\t" + getName() + ": bad organisation found in " + ivyRef.getResource()
-                    + ": expected='" + mrid.getOrganisation() + "' found='"
-                    + md.getModuleRevisionId().getOrganisation() + "'");
-            errors.append("bad organisation: expected='" + mrid.getOrganisation() + "' found='"
-                    + md.getModuleRevisionId().getOrganisation() + "'; ");
-            ok = false;
-        }
-        if (!mrid.getName().equals(md.getModuleRevisionId().getName())) {
-            Message.error("\t" + getName() + ": bad module name found in " + ivyRef.getResource()
-                    + ": expected='" + mrid.getName() + " found='"
-                    + md.getModuleRevisionId().getName() + "'");
-            errors.append("bad module name: expected='" + mrid.getName() + "' found='"
-                    + md.getModuleRevisionId().getName() + "'; ");
-            ok = false;
-        }
-        if (mrid.getBranch() != null
-                && !mrid.getBranch().equals(md.getModuleRevisionId().getBranch())) {
-            Message.error("\t" + getName() + ": bad branch name found in " + ivyRef.getResource()
-                    + ": expected='" + mrid.getBranch() + " found='"
-                    + md.getModuleRevisionId().getBranch() + "'");
-            errors.append("bad branch name: expected='" + mrid.getBranch() + "' found='"
-                    + md.getModuleRevisionId().getBranch() + "'; ");
-            ok = false;
-        }
-        if (ivyRef.getRevision() != null && !ivyRef.getRevision().startsWith("working@")) {
-            ModuleRevisionId expectedMrid = ModuleRevisionId
-                    .newInstance(mrid, ivyRef.getRevision());
-            if (!getVersionMatcher().accept(expectedMrid, md)) {
-                Message.error("\t" + getName() + ": bad revision found in " + ivyRef.getResource()
-                        + ": expected='" + ivyRef.getRevision() + " found='"
-                        + md.getModuleRevisionId().getRevision() + "'");
-                errors.append("bad revision: expected='" + ivyRef.getRevision() + "' found='"
-                        + md.getModuleRevisionId().getRevision() + "'; ");
-                ok = false;
+    private MutableModuleVersionMetaData doGetArtifactMetadata(Artifact artifact, ExternalResource resource) {
+        if (artifact.isMetadata()) {
+            ModuleRevisionId dependencyRevisionId = artifact.getId().getModuleRevisionId();
+            LocallyAvailableExternalResource cachedResource;
+            try {
+                cachedResource = repositoryCacheManager.downloadAndCacheArtifactFile(artifact.getId(), resourceDownloader, resource);
+            } catch (IOException e) {
+                // TODO:DAZ Work out if/when/why this happens
+                LOGGER.warn("Problem while downloading module descriptor: {}: {}", resource, e.getMessage());
+                return null;
             }
-        }
-        if (!getSettings().getStatusManager().isStatus(md.getStatus())) {
-            Message.error("\t" + getName() + ": bad status found in " + ivyRef.getResource()
-                    + ": '" + md.getStatus() + "'");
-            errors.append("bad status: '" + md.getStatus() + "'; ");
-            ok = false;
-        }
-        if (!ok) {
-            throw new ParseException("inconsistent module descriptor file found in '"
-                    + ivyRef.getResource() + "': " + errors, 0);
+
+            return metaDataParser.parseModuleMetaData(cachedResource, new ExternalResourceResolverDescriptorParseContext(nestedResolver, this, dependencyRevisionId));
+        } else {
+            // Create dummy metadata where no metadata artifact exists
+            DefaultModuleDescriptor md = DefaultModuleDescriptor.newDefaultInstance(artifact.getModuleRevisionId());
+            md.setStatus("integration");
+            return new ModuleDescriptorAdapter(artifact.getModuleRevisionId(), md);
         }
     }
 
-    public ResolvedResource findIvyFileRef(DependencyDescriptor dd, ResolveData data) {
-        // This is not used
-        throw new UnsupportedOperationException();
+    private void checkMetadataConsistency(ModuleVersionSelector selector, ModuleVersionMetaData metadata,
+                                          ResolvedArtifact ivyRef) throws MetaDataParseException {
+        List<String> errors = new ArrayList<String>();
+        if (!selector.getGroup().equals(metadata.getId().getGroup())) {
+            errors.add("bad group: expected='" + selector.getGroup() + "' found='" + metadata.getId().getGroup() + "'");
+        }
+        if (!selector.getName().equals(metadata.getId().getName())) {
+            errors.add("bad module name: expected='" + selector.getName() + "' found='" + metadata.getId().getName() + "'");
+        }
+        String revision = ivyRef.artifact.getModuleRevisionId().getRevision();
+        if (revision != null && !revision.startsWith("working@")) {
+            if (!versionMatcher.accept(revision, metadata)) {
+                errors.add("bad version: expected='" + revision + "' found='" + metadata.getId().getVersion() + "'");
+            }
+        }
+        if (!metadata.getStatusScheme().contains(metadata.getStatus())) {
+            errors.add("bad status: '" + metadata.getStatus() + "'; ");
+        }
+        if (errors.size() > 0) {
+            throw new MetaDataParseException(String.format("inconsistent module metadata found. Descriptor: %s Errors: %s",
+                    ivyRef.resource, Joiner.on(SystemProperties.getLineSeparator()).join(errors)));
+        }
     }
 
-    public ResolvedResource findIvyFileRef(DependencyDescriptor dd) {
+    protected ResolvedArtifact findIvyFileRef(DependencyDescriptor dd) {
         ModuleRevisionId mrid = dd.getDependencyRevisionId();
         Artifact artifact = DefaultArtifact.newIvyArtifact(mrid, null);
-        return findResourceUsingPatterns(mrid, ivyPatterns, artifact, getRMDParser(dd), null, true);
+        return findResourceUsingPatterns(DefaultModuleVersionSelector.newSelector(mrid), ivyPatterns, artifact, true);
     }
 
-    protected ResolvedResource findFirstArtifactRef(ModuleDescriptor md) {
-        for (String configuration : md.getConfigurationsNames()) {
-            for (Artifact artifact : md.getArtifacts(configuration)) {
-                ResolvedResource artifactRef = getArtifactRef(artifact, null, false);
-                if (artifactRef != null) {
-                    return artifactRef;
-                }
+    private ResolvedArtifact findAnyArtifact(ModuleDescriptor md) {
+        for (Artifact artifact : md.getAllArtifacts()) {
+            ResolvedArtifact artifactRef = getArtifactRef(artifact, false);
+            if (artifactRef != null) {
+                return artifactRef;
             }
         }
         return null;
     }
 
-    public boolean exists(Artifact artifact) {
-        // This is never used
-        throw new UnsupportedOperationException();
-    }
-
     public ArtifactOrigin locate(Artifact artifact) {
-        ResolvedResource artifactRef = getArtifactRef(artifact, null, false);
-        if (artifactRef != null && artifactRef.getResource().exists()) {
-            final Resource resource = artifactRef.getResource();
+        ResolvedArtifact artifactRef = getArtifactRef(artifact, false);
+        if (artifactRef != null && artifactRef.resource.exists()) {
+            ExternalResource resource = artifactRef.resource;
             return new ArtifactOrigin(artifact, resource.isLocal(), resource.getName());
         }
         return null;
     }
 
-    protected ResolvedResource getArtifactRef(Artifact artifact, Date date) {
-        return getArtifactRef(artifact, date, true);
+    private ResolvedArtifact getArtifactRef(Artifact artifact, boolean forDownload) {
+        ModuleVersionSelector selector = DefaultModuleVersionSelector.newSelector(artifact.getModuleRevisionId());
+        return findResourceUsingPatterns(selector, getArtifactPatterns(), artifact, forDownload);
     }
 
-    protected ResolvedResource getArtifactRef(Artifact artifact, Date date, boolean forDownload) {
-        ModuleRevisionId moduleRevisionId = artifact.getModuleRevisionId();
-        ResourceMDParser parser = getDefaultRMDParser(artifact.getModuleRevisionId().getModuleId());
-        return findResourceUsingPatterns(moduleRevisionId, getArtifactPatterns(), artifact, parser, date, forDownload);
+    protected ResolvedArtifact findResourceUsingPatterns(ModuleVersionSelector selector, List<String> patternList, Artifact artifact, boolean forDownload) {
+        if (versionMatcher.isDynamic(selector.getVersion())) {
+            return findDynamicResourceUsingPatterns(selector, patternList, artifact, forDownload);
+        } else {
+            return findStaticResourceUsingPatterns(selector, patternList, artifact, forDownload);
+        }
     }
 
-    protected ResourceMDParser getRMDParser(final DependencyDescriptor dd) {
-        return new ResourceMDParser() {
-            public MDResolvedResource parse(Resource resource, String rev) {
-                try {
-                    ResolvedModuleRevision rmr = ExternalResourceResolver.this.parse(new ResolvedResource(resource, rev), dd);
-                    if (rmr == null) {
-                        return null;
-                    } else {
-                        return new MDResolvedResource(resource, rev, rmr);
-                    }
-                } catch (ParseException e) {
-                    Message.warn("Failed to parse the file '" + resource + "': "
-                            + e.getMessage());
-                    return null;
-                }
+    private ResolvedArtifact findStaticResourceUsingPatterns(ModuleVersionSelector selector, List<String> patternList, Artifact artifact, boolean forDownload) {
+        // Static version, return first found
+        for (String pattern : patternList) {
+            ResourcePattern resourcePattern = toResourcePattern(pattern);
+            String resourceName = resourcePattern.toPath(artifact);
+            LOGGER.debug("Loading {}", resourceName);
+            ExternalResource resource = getResource(resourceName, artifact.getId(), forDownload);
+            if (resource.exists()) {
+                return new ResolvedArtifact(resource, artifact);
+            } else {
+                LOGGER.debug("Resource not reachable for {}: res={}", selector, resource);
             }
-
-        };
+        }
+        return null;
     }
 
-    protected ResourceMDParser getDefaultRMDParser(final ModuleId mid) {
-        return new ResourceMDParser() {
-            public MDResolvedResource parse(Resource resource, String rev) {
-                DefaultModuleDescriptor md = DefaultModuleDescriptor.newDefaultInstance(new ModuleRevisionId(mid, rev));
-                md.setStatus("integration");
-                MetadataArtifactDownloadReport madr = new MetadataArtifactDownloadReport(md.getMetadataArtifact());
-                madr.setDownloadStatus(DownloadStatus.NO);
-                madr.setSearched(true);
-                return new MDResolvedResource(resource, rev, new ResolvedModuleRevision(ExternalResourceResolver.this, ExternalResourceResolver.this, md, madr, isForce()));
-            }
-        };
+    private ResolvedArtifact findDynamicResourceUsingPatterns(ModuleVersionSelector selector, List<String> patternList, Artifact artifact, boolean forDownload) {
+        // Dynamic version: list all, then choose latest
+        VersionList versionList = listVersionsForAllPatterns(selector, patternList, artifact);
+        return findLatestResource(selector, versionList, artifact, forDownload);
     }
 
-    protected ResolvedResource findResourceUsingPatterns(ModuleRevisionId moduleRevision, List<String> patternList, Artifact artifact, ResourceMDParser rmdparser, Date date, boolean forDownload) {
-        List<ResolvedResource> resolvedResources = new ArrayList<ResolvedResource>();
-        Set<String> foundRevisions = new HashSet<String>();
-        boolean dynamic = getVersionMatcher().isDynamic(moduleRevision);
+    private VersionList listVersionsForAllPatterns(ModuleVersionSelector selector, List<String> patternList, Artifact artifact) {
+        VersionList versionList = versionLister.getVersionList(selector);
         for (String pattern : patternList) {
             ResourcePattern resourcePattern = toResourcePattern(pattern);
-            ResolvedResource rres = findResourceUsingPattern(moduleRevision, resourcePattern, artifact, rmdparser, date, forDownload);
-            if ((rres != null) && !foundRevisions.contains(rres.getRevision())) {
-                // only add the first found ResolvedResource for each revision
-                foundRevisions.add(rres.getRevision());
-                resolvedResources.add(rres);
-                if (!dynamic) {
-                    break;
-                }
-            }
-        }
-
-        if (resolvedResources.size() > 1) {
-            ResolvedResource[] rress = resolvedResources.toArray(new ResolvedResource[resolvedResources.size()]);
-            List<ResolvedResource> sortedResources = getLatestStrategy().sort(rress);
-            // Discard all but the last, which is returned
-            for (int i = 0; i < sortedResources.size() - 1; i++) {
-                ResolvedResource resolvedResource = sortedResources.get(i);
-                discardResource(resolvedResource.getResource());
+            try {
+                versionList.visit(resourcePattern, artifact);
+            } catch (ResourceNotFoundException e) {
+                LOGGER.debug(String.format("Unable to load version list for %s from %s", selector, getRepository()));
+                // Don't add any versions
+                // TODO:DAZ Should fail?
             }
-            return sortedResources.get(sortedResources.size() - 1);
-        } else if (resolvedResources.size() == 1) {
-            return resolvedResources.get(0);
-        } else {
-            return null;
         }
+        return versionList;
     }
 
-    public ResolvedResource findLatestResource(ModuleRevisionId mrid, VersionList versions, ResourceMDParser rmdparser, Date date, ResourcePattern pattern, Artifact artifact, boolean forDownload) {
+    private ResolvedArtifact findLatestResource(ModuleVersionSelector selector, VersionList versions, Artifact artifact, boolean forDownload) {
+        String requestedVersion = selector.getVersion();
         String name = getName();
-        VersionMatcher versionMatcher = getVersionMatcher();
-        List<String> sorted = versions.sortLatestFirst(getLatestStrategy());
-        for (String version : sorted) {
-            ModuleRevisionId foundMrid = ModuleRevisionId.newInstance(mrid, version);
 
-            if (!versionMatcher.accept(mrid, foundMrid)) {
-                LOGGER.debug(name + ": rejected by version matcher: " + version);
+        for (VersionList.ListedVersion listedVersion : versions.sortLatestFirst(latestStrategy)) {
+            String foundVersion = listedVersion.getVersion();
+
+            boolean needsMetadata = versionMatcher.needModuleMetadata(requestedVersion, foundVersion);
+            if (!needsMetadata && !versionMatcher.accept(requestedVersion, foundVersion)) {
+                LOGGER.debug(name + ": rejected by version matcher: " + foundVersion);
                 continue;
             }
 
-            boolean needsModuleDescriptor = versionMatcher.needModuleDescriptor(mrid, foundMrid);
-            artifact = DefaultArtifact.cloneWithAnotherMrid(artifact, foundMrid);
-            String resourcePath = pattern.toPath(artifact);
-            Resource resource = getResource(resourcePath, artifact, forDownload || needsModuleDescriptor);
-            String description = version + " [" + resource + "]";
+            ModuleRevisionId revision = IvyUtil.createModuleRevisionId(selector.getGroup(), selector.getName(), foundVersion, null);
+            artifact = DefaultArtifact.cloneWithAnotherMrid(artifact, revision);
+            String resourcePath = listedVersion.getPattern().toPath(artifact);
+            ExternalResource resource = getResource(resourcePath, artifact.getId(), forDownload || needsMetadata);
+            String description = foundVersion + " [" + resource + "]";
             if (!resource.exists()) {
                 LOGGER.debug(name + ": unreachable: " + description);
                 discardResource(resource);
                 continue;
             }
-            if (date != null && resource.getLastModified() > date.getTime()) {
-                LOGGER.debug(name + ": too young: " + description);
-                discardResource(resource);
-                continue;
-            }
-            if (versionMatcher.needModuleDescriptor(mrid, foundMrid)) {
-                MDResolvedResource parsedResource = rmdparser.parse(resource, version);
-                if (parsedResource == null) {
+            if (needsMetadata) {
+                MutableModuleVersionMetaData metaData = getArtifactMetadata(artifact, resource);
+                if (metaData == null) {
                     LOGGER.debug(name + ": impossible to get module descriptor resource: " + description);
                     discardResource(resource);
                     continue;
                 }
-                ModuleDescriptor md = parsedResource.getResolvedModuleRevision().getDescriptor();
-                if (!versionMatcher.accept(mrid, md)) {
+                if (!versionMatcher.accept(requestedVersion, metaData)) {
                     LOGGER.debug(name + ": md rejected by version matcher: " + description);
                     discardResource(resource);
                     continue;
                 }
 
-                return parsedResource;
+                return new DownloadedAndParsedMetaDataArtifact(resource, artifact, metaData);
             }
-            return new ResolvedResource(resource, version);
+            return new ResolvedArtifact(resource, artifact);
         }
         return null;
     }
 
-    protected ResolvedResource findResourceUsingPattern(ModuleRevisionId moduleRevisionId, ResourcePattern pattern, Artifact artifact, ResourceMDParser resourceParser, Date date, boolean forDownload) {
-        VersionMatcher versionMatcher = getVersionMatcher();
-        if (!versionMatcher.isDynamic(moduleRevisionId)) {
-            return findStaticResourceUsingPattern(moduleRevisionId, pattern, artifact, forDownload);
-        } else {
-            return findDynamicResourceUsingPattern(resourceParser, moduleRevisionId, pattern, artifact, date, forDownload);
-        }
-    }
-
-    private ResolvedResource findStaticResourceUsingPattern(ModuleRevisionId moduleRevisionId, ResourcePattern pattern, Artifact artifact, boolean forDownload) {
-        String resourceName = pattern.toPath(artifact);
-        LOGGER.debug("Loading {}", resourceName);
-        Resource res = getResource(resourceName, artifact, forDownload);
-        if (res.exists()) {
-            String revision = moduleRevisionId.getRevision();
-            return new ResolvedResource(res, revision);
-        } else {
-            LOGGER.debug("Resource not reachable for {}: res={}", moduleRevisionId, res);
-            return null;
-        }
-    }
-
-    private ResolvedResource findDynamicResourceUsingPattern(ResourceMDParser resourceParser, ModuleRevisionId moduleRevisionId, ResourcePattern pattern, Artifact artifact, Date date, boolean forDownload) {
-        VersionList versions = listVersions(moduleRevisionId, pattern, artifact);
-        ResolvedResource found = findLatestResource(moduleRevisionId, versions, resourceParser, date, pattern, artifact, forDownload);
-        if (found == null) {
-            LOGGER.debug("No resource found for {}: pattern={}", moduleRevisionId, pattern);
+    protected void discardResource(ExternalResource resource) {
+        try {
+            resource.close();
+        } catch (IOException e) {
+            LOGGER.warn("Exception closing resource " + resource.getName(), e);
         }
-        return found;
     }
 
-    protected void discardResource(Resource resource) {
-        if (resource instanceof ExternalResource) {
-            try {
-                ((ExternalResource) resource).close();
-            } catch (IOException e) {
-                LOGGER.warn("Exception closing resource " + resource.getName(), e);
-            }
-        }
-    }
+    public void resolve(ArtifactIdentifier artifact, BuildableArtifactResolveResult result, ModuleSource moduleSource) {
+        Artifact ivyArtifact = DefaultArtifactIdentifier.toArtifact(artifact);
 
-    public void resolve(Artifact artifact, BuildableArtifactResolveResult result, ModuleSource moduleSource) {
-        EnhancedArtifactDownloadReport artifactDownloadReport = download(artifact, moduleSource);
-        if (downloadFailed(artifactDownloadReport)) {
-            result.failed(new ArtifactResolveException(artifactDownloadReport.getArtifact(), artifactDownloadReport.getFailure()));
+        File localFile;
+        try {
+            localFile = download(ivyArtifact, moduleSource);
+        } catch (IOException e) {
+            result.failed(new ArtifactResolveException(artifact, e));
             return;
         }
-        File localFile = artifactDownloadReport.getLocalFile();
+
         if (localFile != null) {
             result.resolved(localFile);
         } else {
@@ -495,80 +392,23 @@ public class ExternalResourceResolver implements DependencyResolver {
         }
     }
 
-    protected EnhancedArtifactDownloadReport download(Artifact artifact, ModuleSource moduleSource) {
+    protected File download(Artifact artifact, ModuleSource moduleSource) throws IOException {
         return download(artifact);
     }
 
-    public EnhancedArtifactDownloadReport download(Artifact artifact) {
-        RepositoryCacheManager cacheManager = getRepositoryCacheManager();
-
-        return (EnhancedArtifactDownloadReport) cacheManager.download(artifact, artifactResourceResolver, resourceDownloader, new CacheDownloadOptions());
-    }
-
-    public DownloadReport download(Artifact[] artifacts, DownloadOptions options) {
-        // This is never used
-        throw new UnsupportedOperationException();
-    }
-
-    public ArtifactDownloadReport download(ArtifactOrigin origin, DownloadOptions options) {
-        // This is never used
-        throw new UnsupportedOperationException();
-    }
-
-    public void reportFailure() {
-        // This is never used
-        throw new UnsupportedOperationException();
-    }
-
-    public void reportFailure(Artifact art) {
-        // This is never used
-        throw new UnsupportedOperationException();
-    }
-
-    public String[] listTokenValues(String token, Map otherTokenValues) {
-        // This is never used
-        throw new UnsupportedOperationException();
-    }
-
-    public Map[] listTokenValues(String[] tokens, Map criteria) {
-        // This is never used
-        throw new UnsupportedOperationException();
-    }
-
-    public OrganisationEntry[] listOrganisations() {
-        // This is never used
-        throw new UnsupportedOperationException();
-    }
-
-    public ModuleEntry[] listModules(OrganisationEntry org) {
-        // This is never used
-        throw new UnsupportedOperationException();
-    }
-
-    public RevisionEntry[] listRevisions(ModuleEntry mod) {
-        // This is never used
-        throw new UnsupportedOperationException();
-    }
-
-    public void abortPublishTransaction() throws IOException {
-    }
-
-    public void beginPublishTransaction(ModuleRevisionId module, boolean overwrite) throws IOException {
-    }
-
-    public void commitPublishTransaction() throws IOException {
-    }
+    protected File download(Artifact artifact) throws IOException {
+        ResolvedArtifact artifactRef = getArtifactRef(artifact, true);
+        if (artifactRef == null) {
+            return null;
+        }
 
-    public Namespace getNamespace() {
-        // This is never used
-        throw new UnsupportedOperationException();
+        return repositoryCacheManager.downloadAndCacheArtifactFile(artifact.getId(), resourceDownloader, artifactRef.resource).getLocalResource().getFile();
     }
 
-    protected Resource getResource(String source, Artifact target, boolean forDownload) {
+    private ExternalResource getResource(String source, ArtifactRevisionId target, boolean forDownload) {
         try {
             if (forDownload) {
-                ArtifactRevisionId arid = target.getId();
-                LocallyAvailableResourceCandidates localCandidates = locallyAvailableResourceFinder.findCandidates(arid);
+                LocallyAvailableResourceCandidates localCandidates = locallyAvailableResourceFinder.findCandidates(target);
                 ExternalResource resource = repository.getResource(source, localCandidates);
                 return resource == null ? new MissingExternalResource(source) : resource;
             } else {
@@ -581,47 +421,28 @@ public class ExternalResourceResolver implements DependencyResolver {
         }
     }
 
-    protected VersionList listVersions(ModuleRevisionId moduleRevisionId, ResourcePattern pattern, Artifact artifact) {
-        try {
-            VersionList versionList = versionLister.getVersionList(moduleRevisionId);
-            versionList.visit(pattern, artifact);
-            return versionList;
-        } catch (ResourceNotFoundException e) {
-            LOGGER.debug(String.format("Unable to load version list for %s from %s", moduleRevisionId.getModuleId(), getRepository()));
-            return new DefaultVersionList(Collections.<String>emptyList());
-        }
-    }
-
-    protected long get(Resource resource, File destination) throws IOException {
+    private void get(ExternalResource resource, File destination) throws IOException {
         LOGGER.debug("Downloading {} to {}", resource.getName(), destination);
         if (destination.getParentFile() != null) {
             GFileUtils.mkdirs(destination.getParentFile());
         }
 
-        if (!(resource instanceof ExternalResource)) {
-            throw new IllegalArgumentException("Can only download ExternalResource");
-        }
-
-        ExternalResource externalResource = (ExternalResource) resource;
         try {
-            externalResource.writeTo(destination);
+            resource.writeTo(destination);
         } finally {
-            externalResource.close();
+            resource.close();
         }
-        return destination.length();
     }
 
-    private long getAndCheck(Resource resource, File dest) throws IOException {
-        long size = get(resource, dest);
+    private void verifyChecksums(ExternalResource resource, File dest) throws IOException {
         String[] checksums = getChecksumAlgorithms();
         boolean checked = false;
         for (int i = 0; i < checksums.length && !checked; i++) {
             checked = check(resource, dest, checksums[i]);
         }
-        return size;
     }
 
-    private boolean check(Resource resource, File dest, String algorithm) throws IOException {
+    private boolean check(ExternalResource resource, File dest, String algorithm) throws IOException {
         if (!ChecksumHelper.isKnownAlgorithm(algorithm)) {
             throw new IllegalArgumentException("Unknown checksum algorithm: " + algorithm);
         }
@@ -633,7 +454,7 @@ public class ExternalResourceResolver implements DependencyResolver {
             try {
                 get(checksumResource, csFile);
                 ChecksumHelper.check(dest, csFile, algorithm);
-                Message.verbose(algorithm + " OK for " + resource);
+                LOGGER.debug("{} OK for {}", algorithm, resource);
                 return true;
             } finally {
                 csFile.delete();
@@ -643,7 +464,17 @@ public class ExternalResourceResolver implements DependencyResolver {
         }
     }
 
-    public void publish(Artifact artifact, File src, boolean overwrite) throws IOException {
+    // TODO:DAZ Remove the need for this, by using our own set of PatternMatchers
+    public void setSettings(IvySettings settings) {
+    }
+
+    public void publish(ModuleVersionPublishMetaData moduleVersion) throws IOException {
+        for (Map.Entry<Artifact, File> entry : moduleVersion.getArtifacts().entrySet()) {
+            publish(entry.getKey(), entry.getValue());
+        }
+    }
+
+    private void publish(Artifact artifact, File src) throws IOException {
         String destinationPattern;
         if ("ivy".equals(artifact.getType()) && !getIvyPatterns().isEmpty()) {
             destinationPattern = getIvyPatterns().get(0);
@@ -692,25 +523,20 @@ public class ExternalResourceResolver implements DependencyResolver {
         artifactPatterns = patterns;
     }
 
-    public void dumpSettings() {
-        // this is not used
-        throw new UnsupportedOperationException();
-    }
-
     public boolean isM2compatible() {
-        return m2compatible;
+        return m2Compatible;
     }
 
     public void setM2compatible(boolean compatible) {
-        m2compatible = compatible;
+        m2Compatible = compatible;
     }
 
     public boolean isCheckconsistency() {
-        return checkconsistency;
+        return checkConsistency;
     }
 
     public void setCheckconsistency(boolean checkConsistency) {
-        checkconsistency = checkConsistency;
+        this.checkConsistency = checkConsistency;
     }
 
     public void setForce(boolean force) {
@@ -722,33 +548,11 @@ public class ExternalResourceResolver implements DependencyResolver {
     }
 
     public boolean isAllownomd() {
-        return allownomd;
-    }
-
-    public void setAllownomd(boolean b) {
-        Message.deprecated(
-            "allownomd is deprecated, please use descriptor=\""
-            + (b ? BasicResolver.DESCRIPTOR_OPTIONAL : BasicResolver.DESCRIPTOR_REQUIRED) + "\" instead");
-        allownomd = b;
-    }
-
-    /**
-     * Sets the module descriptor presence rule.
-     * Should be one of {@link BasicResolver#DESCRIPTOR_REQUIRED} or {@link BasicResolver#DESCRIPTOR_OPTIONAL}.
-     *
-     * @param descriptorRule the descriptor rule to use with this resolver.
-     */
-    public void setDescriptor(String descriptorRule) {
-        if (BasicResolver.DESCRIPTOR_REQUIRED.equals(descriptorRule)) {
-          allownomd = false;
-        } else if (BasicResolver.DESCRIPTOR_OPTIONAL.equals(descriptorRule)) {
-          allownomd = true;
-        } else {
-            throw new IllegalArgumentException(
-                "unknown descriptor rule '" + descriptorRule
-                + "'. Allowed rules are: "
-                + Arrays.asList(new String[] {BasicResolver.DESCRIPTOR_REQUIRED, BasicResolver.DESCRIPTOR_OPTIONAL}));
-        }
+        return allowMissingDescriptor;
+    }
+
+    public void setAllownomd(boolean allowMissingDescriptor) {
+        this.allowMissingDescriptor = allowMissingDescriptor;
     }
 
     public String[] getChecksumAlgorithms() {
@@ -772,97 +576,58 @@ public class ExternalResourceResolver implements DependencyResolver {
         this.checksums = checksums;
     }
 
-    public LatestStrategy getLatestStrategy() {
-        if (latestStrategy == null) {
-            initLatestStrategyFromSettings();
-        }
-        return latestStrategy;
-    }
-
-    private void initLatestStrategyFromSettings() {
-        if (getSettings() != null) {
-            if (latestStrategyName != null && !"default".equals(latestStrategyName)) {
-                latestStrategy = getSettings().getLatestStrategy(latestStrategyName);
-                if (latestStrategy == null) {
-                    throw new IllegalStateException(
-                        "unknown latest strategy '" + latestStrategyName + "'");
-                }
-            } else {
-                latestStrategy = getSettings().getDefaultLatestStrategy();
-                Message.debug(getName() + ": no latest strategy defined: using default");
-            }
-        } else {
-            throw new IllegalStateException(
-                "no ivy instance found: "
-                + "impossible to get a latest strategy without ivy instance");
-        }
-    }
-
-    public void setLatestStrategy(LatestStrategy latestStrategy) {
-        this.latestStrategy = latestStrategy;
-    }
-
-    public void setLatest(String strategyName) {
-        latestStrategyName = strategyName;
-    }
-
-    public String getLatest() {
-        if (latestStrategyName == null) {
-            latestStrategyName = "default";
-        }
-        return latestStrategyName;
-    }
-
-    public void setChangingMatcher(String changingMatcherName) {
-        this.changingMatcherName = changingMatcherName;
-    }
-
     public String getChangingMatcherName() {
         return changingMatcherName;
     }
 
-    public void setChangingPattern(String changingPattern) {
-        this.changingPattern = changingPattern;
+    public void setChangingMatcher(String changingMatcherName) {
+        this.changingMatcherName = changingMatcherName;
     }
 
     public String getChangingPattern() {
         return changingPattern;
     }
 
-    public void setCheckmodified(boolean check) {
-        checkmodified = Boolean.valueOf(check);
-    }
-
-    public RepositoryCacheManager getRepositoryCacheManager() {
-        return repositoryCacheManager;
+    public void setChangingPattern(String changingPattern) {
+        this.changingPattern = changingPattern;
     }
 
-    public void setRepositoryCacheManager(RepositoryCacheManager repositoryCacheManager) {
+    public void setRepositoryCacheManager(RepositoryArtifactCache repositoryCacheManager) {
         this.repositoryCacheManager = repositoryCacheManager;
     }
 
-    public void setCache(String cacheName) {
-        cacheManagerName = cacheName;
-    }
-
     protected ResourcePattern toResourcePattern(String pattern) {
         return isM2compatible() ? new M2ResourcePattern(pattern) : new IvyResourcePattern(pattern);
     }
 
-    protected boolean downloadFailed(ArtifactDownloadReport artifactReport) {
-        return artifactReport.getDownloadStatus() == DownloadStatus.FAILED
-                && !artifactReport.getDownloadDetails().equals(ArtifactDownloadReport.MISSING_ARTIFACT);
-    }
-
     private boolean isChanging(ModuleDescriptor moduleDescriptor) {
         if (changingMatcherName == null || changingPattern == null) {
-            return false;
+            return false; // TODO: tell from module metadata (rule)
         }
-        PatternMatcher matcher = settings.getMatcher(changingMatcherName);
+        PatternMatcher matcher = ResolverStrategy.INSTANCE.getPatternMatcher(changingMatcherName);
         if (matcher == null) {
             throw new IllegalStateException("unknown matcher '" + changingMatcherName
                     + "'. It is set as changing matcher in " + this);
         }
         return matcher.getMatcher(changingPattern).matches(moduleDescriptor.getResolvedModuleRevisionId().getRevision());
     }
+
+    protected static class ResolvedArtifact {
+        private final ExternalResource resource;
+        private final Artifact artifact;
+
+        public ResolvedArtifact(ExternalResource resource, Artifact artifact) {
+            this.resource = resource;
+            this.artifact = artifact;
+        }
+    }
+
+    private static class DownloadedAndParsedMetaDataArtifact extends ResolvedArtifact {
+        private final ModuleVersionMetaData moduleVersionMetaData;
+
+        public DownloadedAndParsedMetaDataArtifact(ExternalResource resource, Artifact artifact, ModuleVersionMetaData moduleVersionMetaData) {
+            super(resource, artifact);
+            this.moduleVersionMetaData = moduleVersionMetaData;
+        }
+    }
 }
diff --git a/subprojects/core-impl/src/main/groovy/org/gradle/api/internal/artifacts/repositories/resolver/ExternalResourceResolverDescriptorParseContext.java b/subprojects/core-impl/src/main/groovy/org/gradle/api/internal/artifacts/repositories/resolver/ExternalResourceResolverDescriptorParseContext.java
new file mode 100644
index 0000000..d828030
--- /dev/null
+++ b/subprojects/core-impl/src/main/groovy/org/gradle/api/internal/artifacts/repositories/resolver/ExternalResourceResolverDescriptorParseContext.java
@@ -0,0 +1,58 @@
+/*
+ * Copyright 2013 the original author or authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY 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.resolver;
+
+import org.apache.ivy.core.cache.ArtifactOrigin;
+import org.apache.ivy.core.module.descriptor.Artifact;
+import org.apache.ivy.core.module.descriptor.DefaultDependencyDescriptor;
+import org.apache.ivy.core.module.descriptor.ModuleDescriptor;
+import org.apache.ivy.core.module.id.ModuleRevisionId;
+import org.gradle.api.internal.artifacts.ivyservice.DefaultBuildableModuleVersionResolveResult;
+import org.gradle.api.internal.artifacts.ivyservice.DependencyToModuleVersionResolver;
+import org.gradle.api.internal.artifacts.ivyservice.ivyresolve.DefaultDependencyMetaData;
+import org.gradle.api.internal.artifacts.ivyservice.ivyresolve.parser.AbstractDescriptorParseContext;
+
+/**
+ * ParserSettings that control the scope of searches carried out during parsing.
+ * If the parser asks for a resolver for the currently resolving revision, the resolver scope is only the repository where the module was resolved.
+ * If the parser asks for a resolver for a different revision, the resolver scope is all repositories.
+ */
+public class ExternalResourceResolverDescriptorParseContext extends AbstractDescriptorParseContext {
+    private final DependencyToModuleVersionResolver mainResolver;
+    private final ExternalResourceResolver moduleResolver;
+    private final ModuleRevisionId moduleRevisionId;
+
+    public ExternalResourceResolverDescriptorParseContext(DependencyToModuleVersionResolver mainResolver, ExternalResourceResolver moduleResolver, ModuleRevisionId moduleRevisionId) {
+        super("integration");
+        this.mainResolver = mainResolver;
+        this.moduleResolver = moduleResolver;
+        this.moduleRevisionId = moduleRevisionId;
+    }
+
+    public ModuleRevisionId getCurrentRevisionId() {
+        return moduleRevisionId;
+    }
+
+    public ModuleDescriptor getModuleDescriptor(ModuleRevisionId moduleRevisionId) {
+        DefaultBuildableModuleVersionResolveResult result = new DefaultBuildableModuleVersionResolveResult();
+        mainResolver.resolve(new DefaultDependencyMetaData(new DefaultDependencyDescriptor(moduleRevisionId, true)), result);
+        return result.getMetaData().getDescriptor();
+    }
+
+    public boolean artifactExists(Artifact artifact) {
+        return !ArtifactOrigin.isUnknown(moduleResolver.locate(artifact));
+    }
+}
diff --git a/subprojects/core-impl/src/main/groovy/org/gradle/api/internal/artifacts/repositories/resolver/IvyResolver.java b/subprojects/core-impl/src/main/groovy/org/gradle/api/internal/artifacts/repositories/resolver/IvyResolver.java
index e5fb66b..7b8cdd5 100644
--- a/subprojects/core-impl/src/main/groovy/org/gradle/api/internal/artifacts/repositories/resolver/IvyResolver.java
+++ b/subprojects/core-impl/src/main/groovy/org/gradle/api/internal/artifacts/repositories/resolver/IvyResolver.java
@@ -16,6 +16,8 @@
 package org.gradle.api.internal.artifacts.repositories.resolver;
 
 import org.apache.ivy.core.module.id.ArtifactRevisionId;
+import org.gradle.api.internal.artifacts.ModuleMetadataProcessor;
+import org.gradle.api.internal.artifacts.ivyservice.ivyresolve.parser.MetaDataParser;
 import org.gradle.api.internal.artifacts.repositories.transport.RepositoryTransport;
 import org.gradle.api.internal.externalresource.local.LocallyAvailableResourceFinder;
 
@@ -24,13 +26,22 @@ import java.net.URI;
 public class IvyResolver extends ExternalResourceResolver implements PatternBasedResolver {
 
     private final RepositoryTransport transport;
+    private final boolean dynamicResolve;
 
     public IvyResolver(String name, RepositoryTransport transport,
-                       LocallyAvailableResourceFinder<ArtifactRevisionId> locallyAvailableResourceFinder
+                       LocallyAvailableResourceFinder<ArtifactRevisionId> locallyAvailableResourceFinder,
+                       MetaDataParser metaDataParser, ModuleMetadataProcessor metadataProcessor,
+                       boolean dynamicResolve
     ) {
-        super(name, transport.getRepository(), new ResourceVersionLister(transport.getRepository()), locallyAvailableResourceFinder);
+        super(name, transport.getRepository(), new ResourceVersionLister(transport.getRepository()), locallyAvailableResourceFinder, metaDataParser, metadataProcessor);
         this.transport = transport;
         this.transport.configureCacheManager(this);
+        this.dynamicResolve = dynamicResolve;
+    }
+
+    @Override
+    public boolean isDynamicResolveMode() {
+        return dynamicResolve;
     }
 
     public void addArtifactLocation(URI baseUri, String pattern) {
diff --git a/subprojects/core-impl/src/main/groovy/org/gradle/api/internal/artifacts/repositories/resolver/MavenMetadataLoader.java b/subprojects/core-impl/src/main/groovy/org/gradle/api/internal/artifacts/repositories/resolver/MavenMetadataLoader.java
index d4cb7d6..9e51bd9 100644
--- a/subprojects/core-impl/src/main/groovy/org/gradle/api/internal/artifacts/repositories/resolver/MavenMetadataLoader.java
+++ b/subprojects/core-impl/src/main/groovy/org/gradle/api/internal/artifacts/repositories/resolver/MavenMetadataLoader.java
@@ -16,9 +16,9 @@
 
 package org.gradle.api.internal.artifacts.repositories.resolver;
 
-import org.apache.ivy.plugins.repository.Resource;
 import org.apache.ivy.util.ContextualSAXHandler;
 import org.apache.ivy.util.XMLHelper;
+import org.gradle.api.internal.ErroringAction;
 import org.gradle.api.internal.externalresource.ExternalResource;
 import org.gradle.api.internal.externalresource.transport.ExternalResourceRepository;
 import org.gradle.api.internal.resource.ResourceException;
@@ -64,23 +64,26 @@ class MavenMetadataLoader {
         }
     }
 
-    private void parseMavenMetadataInto(Resource metadataResource, final MavenMetadata mavenMetadata) throws IOException, SAXException, ParserConfigurationException {
+    private void parseMavenMetadataInto(ExternalResource metadataResource, final MavenMetadata mavenMetadata) throws IOException, SAXException, ParserConfigurationException {
         LOGGER.debug("parsing maven-metadata: {}", metadataResource);
-        InputStream metadataStream = metadataResource.openStream();
-        XMLHelper.parse(metadataStream, null, new ContextualSAXHandler() {
-            public void endElement(String uri, String localName, String qName)
-                    throws SAXException {
-                if ("metadata/versioning/snapshot/timestamp".equals(getContext())) {
-                    mavenMetadata.timestamp = getText();
-                }
-                if ("metadata/versioning/snapshot/buildNumber".equals(getContext())) {
-                    mavenMetadata.buildNumber = getText();
-                }
-                if ("metadata/versioning/versions/version".equals(getContext())) {
-                    mavenMetadata.versions.add(getText().trim());
-                }
-                super.endElement(uri, localName, qName);
+        metadataResource.withContent(new ErroringAction<InputStream>() {
+            public void doExecute(InputStream inputStream) throws ParserConfigurationException, SAXException, IOException {
+                XMLHelper.parse(inputStream, null, new ContextualSAXHandler() {
+                    public void endElement(String uri, String localName, String qName)
+                            throws SAXException {
+                        if ("metadata/versioning/snapshot/timestamp".equals(getContext())) {
+                            mavenMetadata.timestamp = getText();
+                        }
+                        if ("metadata/versioning/snapshot/buildNumber".equals(getContext())) {
+                            mavenMetadata.buildNumber = getText();
+                        }
+                        if ("metadata/versioning/versions/version".equals(getContext())) {
+                            mavenMetadata.versions.add(getText().trim());
+                        }
+                        super.endElement(uri, localName, qName);
+                    }
+                }, null);
             }
-        }, null);
+        });
     }
 }
diff --git a/subprojects/core-impl/src/main/groovy/org/gradle/api/internal/artifacts/repositories/resolver/MavenResolver.java b/subprojects/core-impl/src/main/groovy/org/gradle/api/internal/artifacts/repositories/resolver/MavenResolver.java
index 71eb83f..125eeb1 100644
--- a/subprojects/core-impl/src/main/groovy/org/gradle/api/internal/artifacts/repositories/resolver/MavenResolver.java
+++ b/subprojects/core-impl/src/main/groovy/org/gradle/api/internal/artifacts/repositories/resolver/MavenResolver.java
@@ -21,11 +21,11 @@ import org.apache.ivy.core.module.descriptor.DependencyDescriptor;
 import org.apache.ivy.core.module.id.ArtifactRevisionId;
 import org.apache.ivy.core.module.id.ModuleRevisionId;
 import org.apache.ivy.plugins.matcher.PatternMatcher;
-import org.apache.ivy.plugins.resolver.util.ResolvedResource;
-import org.apache.ivy.plugins.resolver.util.ResourceMDParser;
-import org.gradle.api.internal.artifacts.ivyservice.ivyresolve.BuildableModuleVersionMetaData;
+import org.gradle.api.internal.artifacts.DefaultModuleVersionSelector;
+import org.gradle.api.internal.artifacts.ModuleMetadataProcessor;
+import org.gradle.api.internal.artifacts.ivyservice.ivyresolve.BuildableModuleVersionMetaDataResolveResult;
 import org.gradle.api.internal.artifacts.ivyservice.ivyresolve.ModuleSource;
-import org.gradle.api.internal.artifacts.repositories.cachemanager.EnhancedArtifactDownloadReport;
+import org.gradle.api.internal.artifacts.ivyservice.ivyresolve.parser.MetaDataParser;
 import org.gradle.api.internal.artifacts.repositories.transport.RepositoryTransport;
 import org.gradle.api.internal.externalresource.local.LocallyAvailableResourceFinder;
 import org.gradle.api.internal.resource.ResourceNotFoundException;
@@ -35,6 +35,8 @@ import org.gradle.util.WrapUtil;
 import org.slf4j.Logger;
 import org.slf4j.LoggerFactory;
 
+import java.io.File;
+import java.io.IOException;
 import java.net.URI;
 import java.util.*;
 
@@ -49,11 +51,14 @@ public class MavenResolver extends ExternalResourceResolver implements PatternBa
     private final MavenMetadataLoader mavenMetaDataLoader;
 
     public MavenResolver(String name, URI rootUri, RepositoryTransport transport,
-                         LocallyAvailableResourceFinder<ArtifactRevisionId> locallyAvailableResourceFinder) {
+                         LocallyAvailableResourceFinder<ArtifactRevisionId> locallyAvailableResourceFinder,
+                         MetaDataParser metaDataParser, ModuleMetadataProcessor metadataProcessor
+    ) {
         super(name,
                 transport.getRepository(),
                 new ChainedVersionLister(new MavenVersionLister(transport.getRepository()), new ResourceVersionLister(transport.getRepository())),
-                locallyAvailableResourceFinder);
+                locallyAvailableResourceFinder,
+                metaDataParser, metadataProcessor);
         transport.configureCacheManager(this);
 
         this.mavenMetaDataLoader = new MavenMetadataLoader(transport.getRepository());
@@ -69,7 +74,7 @@ public class MavenResolver extends ExternalResourceResolver implements PatternBa
         updatePatterns();
     }
 
-    public void getDependency(DependencyDescriptor dd, BuildableModuleVersionMetaData result) {
+    protected void getDependency(DependencyDescriptor dd, BuildableModuleVersionMetaDataResolveResult result) {
         if (isSnapshotVersion(dd)) {
             getSnapshotDependency(dd, result);
         } else {
@@ -77,13 +82,13 @@ public class MavenResolver extends ExternalResourceResolver implements PatternBa
         }
     }
 
-    private void getSnapshotDependency(DependencyDescriptor dd, BuildableModuleVersionMetaData result) {
+    private void getSnapshotDependency(DependencyDescriptor dd, BuildableModuleVersionMetaDataResolveResult result) {
         final ModuleRevisionId dependencyRevisionId = dd.getDependencyRevisionId();
         final String uniqueSnapshotVersion = findUniqueSnapshotVersion(dependencyRevisionId);
         if (uniqueSnapshotVersion != null) {
             DependencyDescriptor enrichedDependencyDescriptor = enrichDependencyDescriptorWithSnapshotVersionInfo(dd, dependencyRevisionId, uniqueSnapshotVersion);
             super.getDependency(enrichedDependencyDescriptor, result);
-            if (result.getState() == BuildableModuleVersionMetaData.State.Resolved) {
+            if (result.getState() == BuildableModuleVersionMetaDataResolveResult.State.Resolved) {
                 result.setModuleSource(new TimestampedModuleSource(uniqueSnapshotVersion));
             }
         } else {
@@ -102,20 +107,17 @@ public class MavenResolver extends ExternalResourceResolver implements PatternBa
         return dd.getDependencyRevisionId().getRevision().endsWith("SNAPSHOT");
     }
 
-    protected EnhancedArtifactDownloadReport download(Artifact artifact, ModuleSource moduleSource) {
-        EnhancedArtifactDownloadReport artifactDownloadReport;
-
+    protected File download(Artifact artifact, ModuleSource moduleSource) throws IOException {
         if (moduleSource instanceof TimestampedModuleSource) {
             TimestampedModuleSource timestampedModuleSource = (TimestampedModuleSource) moduleSource;
             String timestampedVersion = timestampedModuleSource.getTimestampedVersion();
-            artifactDownloadReport = downloadTimestampedVersion(artifact, timestampedVersion);
+            return downloadTimestampedVersion(artifact, timestampedVersion);
         } else {
-            artifactDownloadReport = download(artifact);
+            return download(artifact);
         }
-        return artifactDownloadReport;
     }
 
-    private EnhancedArtifactDownloadReport downloadTimestampedVersion(Artifact artifact, String timestampedVersion) {
+    private File downloadTimestampedVersion(Artifact artifact, String timestampedVersion) throws IOException {
         final ModuleRevisionId artifactModuleRevisionId = artifact.getModuleRevisionId();
         final ModuleRevisionId moduleRevisionId = ModuleRevisionId.newInstance(artifactModuleRevisionId.getOrganisation(),
                 artifactModuleRevisionId.getName(),
@@ -157,13 +159,12 @@ public class MavenResolver extends ExternalResourceResolver implements PatternBa
         setArtifactPatterns(artifactPatterns);
     }
 
-    public ResolvedResource findIvyFileRef(DependencyDescriptor dd) {
+    protected ResolvedArtifact findIvyFileRef(DependencyDescriptor dd) {
         if (isUsepoms()) {
             ModuleRevisionId moduleRevisionId = dd.getDependencyRevisionId();
-            //we might need a own implementation of DefaultArtifact here as there is no way to pass extraAttributes AND isMetaData to DefaultArtifact
-            Artifact pomArtifact = new DefaultArtifact(moduleRevisionId, null, moduleRevisionId.getName(), "pom", "pom", moduleRevisionId.getExtraAttributes());
-            ResourceMDParser parser = getRMDParser(dd);
-            return findResourceUsingPatterns(moduleRevisionId, getIvyPatterns(), pomArtifact, parser, null, true);
+            ArtifactRevisionId artifactRevisionId = ArtifactRevisionId.newInstance(moduleRevisionId, moduleRevisionId.getName(), "pom", "pom", moduleRevisionId.getExtraAttributes());
+            Artifact pomArtifact = new DefaultArtifact(artifactRevisionId, null, null, true);
+            return findResourceUsingPatterns(DefaultModuleVersionSelector.newSelector(moduleRevisionId), getIvyPatterns(), pomArtifact, true);
         }
 
         return null;
@@ -191,7 +192,7 @@ public class MavenResolver extends ExternalResourceResolver implements PatternBa
             } catch (ResourceNotFoundException e) {
                 return new MavenMetadata();
             } catch (ResourceException e) {
-                LOGGER.warn("impossible to access maven metadata file, ignored.", e);
+                LOGGER.warn("impossible to access Maven metadata file, ignored.", e);
             }
         }
         return new MavenMetadata();
diff --git a/subprojects/core-impl/src/main/groovy/org/gradle/api/internal/artifacts/repositories/resolver/MavenVersionLister.java b/subprojects/core-impl/src/main/groovy/org/gradle/api/internal/artifacts/repositories/resolver/MavenVersionLister.java
index 2609b47..07c9d5c 100644
--- a/subprojects/core-impl/src/main/groovy/org/gradle/api/internal/artifacts/repositories/resolver/MavenVersionLister.java
+++ b/subprojects/core-impl/src/main/groovy/org/gradle/api/internal/artifacts/repositories/resolver/MavenVersionLister.java
@@ -17,7 +17,7 @@
 package org.gradle.api.internal.artifacts.repositories.resolver;
 
 import org.apache.ivy.core.module.descriptor.Artifact;
-import org.apache.ivy.core.module.id.ModuleRevisionId;
+import org.gradle.api.artifacts.ModuleVersionSelector;
 import org.gradle.api.internal.externalresource.transport.ExternalResourceRepository;
 import org.gradle.api.internal.resource.ResourceException;
 import org.gradle.api.internal.resource.ResourceNotFoundException;
@@ -32,7 +32,7 @@ public class MavenVersionLister implements VersionLister {
         this.mavenMetadataLoader = new MavenMetadataLoader(repository);
     }
 
-    public VersionList getVersionList(final ModuleRevisionId moduleRevisionId) {
+    public VersionList getVersionList(ModuleVersionSelector selector) {
         return new DefaultVersionList() {
             final Set<String> searched = new HashSet<String>();
 
@@ -42,7 +42,9 @@ public class MavenVersionLister implements VersionLister {
                     return;
                 }
                 MavenMetadata mavenMetaData = mavenMetadataLoader.load(metadataLocation);
-                add(mavenMetaData.versions);
+                for (String version : mavenMetaData.versions) {
+                    add(new ListedVersion(version, resourcePattern));
+                }
             }
         };
     }
diff --git a/subprojects/core-impl/src/main/groovy/org/gradle/api/internal/artifacts/repositories/resolver/PatternBasedResolver.java b/subprojects/core-impl/src/main/groovy/org/gradle/api/internal/artifacts/repositories/resolver/PatternBasedResolver.java
index e71e0e0..3bdb961 100644
--- a/subprojects/core-impl/src/main/groovy/org/gradle/api/internal/artifacts/repositories/resolver/PatternBasedResolver.java
+++ b/subprojects/core-impl/src/main/groovy/org/gradle/api/internal/artifacts/repositories/resolver/PatternBasedResolver.java
@@ -15,12 +15,10 @@
  */
 package org.gradle.api.internal.artifacts.repositories.resolver;
 
-import org.apache.ivy.plugins.resolver.DependencyResolver;
-
 import java.net.URI;
 import java.util.List;
 
-public interface PatternBasedResolver extends DependencyResolver {
+public interface PatternBasedResolver {
     void addArtifactLocation(URI baseUri, String pattern);
 
     void addDescriptorLocation(URI baseUri, String pattern);
diff --git a/subprojects/core-impl/src/main/groovy/org/gradle/api/internal/artifacts/repositories/resolver/ResourceVersionLister.java b/subprojects/core-impl/src/main/groovy/org/gradle/api/internal/artifacts/repositories/resolver/ResourceVersionLister.java
index 3034b1a..cd30f39 100644
--- a/subprojects/core-impl/src/main/groovy/org/gradle/api/internal/artifacts/repositories/resolver/ResourceVersionLister.java
+++ b/subprojects/core-impl/src/main/groovy/org/gradle/api/internal/artifacts/repositories/resolver/ResourceVersionLister.java
@@ -18,7 +18,7 @@ package org.gradle.api.internal.artifacts.repositories.resolver;
 
 import org.apache.ivy.core.IvyPatternHelper;
 import org.apache.ivy.core.module.descriptor.Artifact;
-import org.apache.ivy.core.module.id.ModuleRevisionId;
+import org.gradle.api.artifacts.ModuleVersionSelector;
 import org.gradle.api.internal.externalresource.transport.ExternalResourceRepository;
 import org.gradle.api.internal.resource.ResourceException;
 import org.gradle.api.internal.resource.ResourceNotFoundException;
@@ -42,7 +42,7 @@ public class ResourceVersionLister implements VersionLister {
         this.repository = repository;
     }
 
-    public VersionList getVersionList(final ModuleRevisionId moduleRevisionId) {
+    public VersionList getVersionList(ModuleVersionSelector selector) {
         return new DefaultVersionList() {
             final Set<String> directories = new HashSet<String>();
 
@@ -51,7 +51,9 @@ public class ResourceVersionLister implements VersionLister {
                 LOGGER.debug("Listing all in {}", partiallyResolvedPattern);
                 try {
                     List<String> versionStrings = listRevisionToken(partiallyResolvedPattern);
-                    add(versionStrings);
+                    for (String versionString : versionStrings) {
+                        add(new ListedVersion(versionString, resourcePattern));
+                    }
                 } catch (ResourceNotFoundException e) {
                     throw e;
                 } catch (Exception e) {
diff --git a/subprojects/core-impl/src/main/groovy/org/gradle/api/internal/artifacts/repositories/resolver/VersionList.java b/subprojects/core-impl/src/main/groovy/org/gradle/api/internal/artifacts/repositories/resolver/VersionList.java
index 16bf49d..1bd9cf2 100644
--- a/subprojects/core-impl/src/main/groovy/org/gradle/api/internal/artifacts/repositories/resolver/VersionList.java
+++ b/subprojects/core-impl/src/main/groovy/org/gradle/api/internal/artifacts/repositories/resolver/VersionList.java
@@ -17,7 +17,8 @@
 package org.gradle.api.internal.artifacts.repositories.resolver;
 
 import org.apache.ivy.core.module.descriptor.Artifact;
-import org.apache.ivy.plugins.latest.LatestStrategy;
+import org.gradle.api.internal.artifacts.ivyservice.ivyresolve.strategy.LatestStrategy;
+import org.gradle.api.internal.artifacts.ivyservice.ivyresolve.Versioned;
 import org.gradle.api.internal.resource.ResourceException;
 import org.gradle.api.internal.resource.ResourceNotFoundException;
 
@@ -33,9 +34,41 @@ public interface VersionList {
      */
     void visit(ResourcePattern pattern, Artifact artifact) throws ResourceNotFoundException, ResourceException;
 
-    Set<String> getVersionStrings();
+    Set<ListedVersion> getVersions();
 
     boolean isEmpty();
 
-    List<String> sortLatestFirst(LatestStrategy latestStrategy);
+    List<ListedVersion> sortLatestFirst(LatestStrategy latestStrategy);
+
+    class ListedVersion implements Versioned {
+        private String version;
+        private ResourcePattern pattern;
+
+        public ListedVersion(String version, ResourcePattern pattern) {
+            this.pattern = pattern;
+            this.version = version;
+        }
+
+        public String getVersion() {
+            return version;
+        }
+
+        /**
+         * @return The pattern that can be used to load this version.
+         */
+        public ResourcePattern getPattern() {
+            return pattern;
+        }
+
+        public boolean equals(Object other) {
+            if (!(other instanceof ListedVersion)) {
+                return false;
+            }
+            return version.equals(((ListedVersion) other).getVersion());
+        }
+
+        public int hashCode() {
+            return version.hashCode();
+        }
+    }
 }
diff --git a/subprojects/core-impl/src/main/groovy/org/gradle/api/internal/artifacts/repositories/resolver/VersionLister.java b/subprojects/core-impl/src/main/groovy/org/gradle/api/internal/artifacts/repositories/resolver/VersionLister.java
index 975c336..9b16869 100644
--- a/subprojects/core-impl/src/main/groovy/org/gradle/api/internal/artifacts/repositories/resolver/VersionLister.java
+++ b/subprojects/core-impl/src/main/groovy/org/gradle/api/internal/artifacts/repositories/resolver/VersionLister.java
@@ -16,11 +16,11 @@
 
 package org.gradle.api.internal.artifacts.repositories.resolver;
 
-import org.apache.ivy.core.module.id.ModuleRevisionId;
+import org.gradle.api.artifacts.ModuleVersionSelector;
 
 public interface VersionLister {
     /**
-     * Creates an empty version list for the given module version. Call {@link VersionList#visit(String, org.apache.ivy.core.module.descriptor.Artifact)} to search for versions.
+     * Creates an empty version list for the given module version. Call {@link VersionList#visit(ResourcePattern, org.apache.ivy.core.module.descriptor.Artifact)} to search for versions.
      */
-    VersionList getVersionList(ModuleRevisionId moduleRevisionId);
+    VersionList getVersionList(ModuleVersionSelector selector);
 }
diff --git a/subprojects/core-impl/src/main/groovy/org/gradle/api/internal/artifacts/repositories/transport/RepositoryTransportFactory.java b/subprojects/core-impl/src/main/groovy/org/gradle/api/internal/artifacts/repositories/transport/RepositoryTransportFactory.java
index 3a645bc..21fe348 100644
--- a/subprojects/core-impl/src/main/groovy/org/gradle/api/internal/artifacts/repositories/transport/RepositoryTransportFactory.java
+++ b/subprojects/core-impl/src/main/groovy/org/gradle/api/internal/artifacts/repositories/transport/RepositoryTransportFactory.java
@@ -15,8 +15,8 @@
  */
 package org.gradle.api.internal.artifacts.repositories.transport;
 
-import org.apache.ivy.core.cache.RepositoryCacheManager;
 import org.gradle.api.artifacts.repositories.PasswordCredentials;
+import org.gradle.api.internal.artifacts.repositories.cachemanager.RepositoryArtifactCache;
 import org.gradle.api.internal.externalresource.cached.CachedExternalResourceIndex;
 import org.gradle.api.internal.externalresource.transport.file.FileTransport;
 import org.gradle.api.internal.externalresource.transport.http.HttpTransport;
@@ -24,15 +24,15 @@ import org.gradle.api.internal.file.TemporaryFileProvider;
 import org.gradle.logging.ProgressLoggerFactory;
 
 public class RepositoryTransportFactory {
-    private final RepositoryCacheManager downloadingCacheManager;
+    private final RepositoryArtifactCache downloadingCacheManager;
     private final TemporaryFileProvider temporaryFileProvider;
     private final CachedExternalResourceIndex<String> cachedExternalResourceIndex;
-    private final RepositoryCacheManager localCacheManager;
+    private final RepositoryArtifactCache localCacheManager;
     private final ProgressLoggerFactory progressLoggerFactory;
 
     public RepositoryTransportFactory(ProgressLoggerFactory progressLoggerFactory,
-                                      RepositoryCacheManager localCacheManager,
-                                      RepositoryCacheManager downloadingCacheManager,
+                                      RepositoryArtifactCache localCacheManager,
+                                      RepositoryArtifactCache downloadingCacheManager,
                                       TemporaryFileProvider temporaryFileProvider,
                                       CachedExternalResourceIndex<String> cachedExternalResourceIndex) {
         this.progressLoggerFactory = progressLoggerFactory;
diff --git a/subprojects/core-impl/src/main/groovy/org/gradle/api/internal/artifacts/result/AbstractDependencyResult.java b/subprojects/core-impl/src/main/groovy/org/gradle/api/internal/artifacts/result/AbstractDependencyResult.java
index 4ccb18f..5b86df5 100644
--- a/subprojects/core-impl/src/main/groovy/org/gradle/api/internal/artifacts/result/AbstractDependencyResult.java
+++ b/subprojects/core-impl/src/main/groovy/org/gradle/api/internal/artifacts/result/AbstractDependencyResult.java
@@ -20,9 +20,6 @@ import org.gradle.api.artifacts.ModuleVersionSelector;
 import org.gradle.api.artifacts.result.DependencyResult;
 import org.gradle.api.artifacts.result.ResolvedModuleVersionResult;
 
-/**
- * by Szczepan Faber, created at: 7/26/12
- */
 public class AbstractDependencyResult implements DependencyResult {
     private final ModuleVersionSelector requested;
     private final ResolvedModuleVersionResult from;
diff --git a/subprojects/core-impl/src/main/groovy/org/gradle/api/internal/artifacts/result/DefaultResolutionResult.java b/subprojects/core-impl/src/main/groovy/org/gradle/api/internal/artifacts/result/DefaultResolutionResult.java
index 09d2ce0..3cf2feb 100644
--- a/subprojects/core-impl/src/main/groovy/org/gradle/api/internal/artifacts/result/DefaultResolutionResult.java
+++ b/subprojects/core-impl/src/main/groovy/org/gradle/api/internal/artifacts/result/DefaultResolutionResult.java
@@ -24,25 +24,23 @@ import org.gradle.api.artifacts.result.ResolvedDependencyResult;
 import org.gradle.api.artifacts.result.ResolvedModuleVersionResult;
 import org.gradle.api.internal.Actions;
 import org.gradle.api.internal.ClosureBackedAction;
+import org.gradle.internal.Factory;
 
 import java.util.HashSet;
 import java.util.LinkedHashSet;
 import java.util.Set;
 
-/**
- * by Szczepan Faber, created at: 8/10/12
- */
 public class DefaultResolutionResult implements ResolutionResult {
 
-    private final ResolvedModuleVersionResult root;
+    private Factory<ResolvedModuleVersionResult> rootSource;
 
-    public DefaultResolutionResult(ResolvedModuleVersionResult root) {
-        assert root != null;
-        this.root = root;
+    public DefaultResolutionResult(Factory<ResolvedModuleVersionResult> rootSource) {
+        assert rootSource != null;
+        this.rootSource = rootSource;
     }
 
     public ResolvedModuleVersionResult getRoot() {
-        return root;
+        return rootSource.create();
     }
 
     public Set<? extends DependencyResult> getAllDependencies() {
@@ -56,7 +54,7 @@ public class DefaultResolutionResult implements ResolutionResult {
     }
 
     public void allDependencies(Action<? super DependencyResult> action) {
-        eachElement(root, Actions.doNothing(), action, new HashSet<ResolvedModuleVersionResult>());
+        eachElement(getRoot(), Actions.doNothing(), action, new HashSet<ResolvedModuleVersionResult>());
     }
 
     public void allDependencies(final Closure closure) {
@@ -80,12 +78,12 @@ public class DefaultResolutionResult implements ResolutionResult {
 
     public Set<ResolvedModuleVersionResult> getAllModuleVersions() {
         final Set<ResolvedModuleVersionResult> out = new LinkedHashSet<ResolvedModuleVersionResult>();
-        eachElement(root, Actions.doNothing(), Actions.doNothing(), out);
+        eachElement(getRoot(), Actions.doNothing(), Actions.doNothing(), out);
         return out;
     }
 
     public void allModuleVersions(final Action<? super ResolvedModuleVersionResult> action) {
-        eachElement(root, action, Actions.doNothing(), new HashSet<ResolvedModuleVersionResult>());
+        eachElement(getRoot(), action, Actions.doNothing(), new HashSet<ResolvedModuleVersionResult>());
     }
 
     public void allModuleVersions(final Closure closure) {
diff --git a/subprojects/core-impl/src/main/groovy/org/gradle/api/internal/artifacts/result/DefaultResolvedDependencyResult.java b/subprojects/core-impl/src/main/groovy/org/gradle/api/internal/artifacts/result/DefaultResolvedDependencyResult.java
index 074e5e4..5a8d47f 100644
--- a/subprojects/core-impl/src/main/groovy/org/gradle/api/internal/artifacts/result/DefaultResolvedDependencyResult.java
+++ b/subprojects/core-impl/src/main/groovy/org/gradle/api/internal/artifacts/result/DefaultResolvedDependencyResult.java
@@ -20,9 +20,6 @@ import org.gradle.api.artifacts.ModuleVersionSelector;
 import org.gradle.api.artifacts.result.ResolvedDependencyResult;
 import org.gradle.api.artifacts.result.ResolvedModuleVersionResult;
 
-/**
- * by Szczepan Faber, created at: 7/26/12
- */
 public class DefaultResolvedDependencyResult extends AbstractDependencyResult implements ResolvedDependencyResult {
     private final ResolvedModuleVersionResult selected;
 
diff --git a/subprojects/core-impl/src/main/groovy/org/gradle/api/internal/artifacts/result/DefaultResolvedModuleVersionResult.java b/subprojects/core-impl/src/main/groovy/org/gradle/api/internal/artifacts/result/DefaultResolvedModuleVersionResult.java
index 0bff383..18671fe 100644
--- a/subprojects/core-impl/src/main/groovy/org/gradle/api/internal/artifacts/result/DefaultResolvedModuleVersionResult.java
+++ b/subprojects/core-impl/src/main/groovy/org/gradle/api/internal/artifacts/result/DefaultResolvedModuleVersionResult.java
@@ -21,25 +21,17 @@ import org.gradle.api.artifacts.result.DependencyResult;
 import org.gradle.api.artifacts.result.ModuleVersionSelectionReason;
 import org.gradle.api.artifacts.result.ResolvedDependencyResult;
 import org.gradle.api.artifacts.result.ResolvedModuleVersionResult;
-import org.gradle.api.internal.artifacts.ivyservice.resolveengine.result.VersionSelectionReasons;
 
 import java.util.Collections;
 import java.util.LinkedHashSet;
 import java.util.Set;
 
-/**
- * by Szczepan Faber, created at: 8/10/12
- */
 public class DefaultResolvedModuleVersionResult implements ResolvedModuleVersionResult {
     private final ModuleVersionIdentifier id;
     private final Set<DependencyResult> dependencies = new LinkedHashSet<DependencyResult>();
     private final Set<ResolvedDependencyResult> dependents = new LinkedHashSet<ResolvedDependencyResult>();
     private final ModuleVersionSelectionReason selectionReason;
 
-    public DefaultResolvedModuleVersionResult(ModuleVersionIdentifier id) {
-        this(id, VersionSelectionReasons.REQUESTED);
-    }
-
     public DefaultResolvedModuleVersionResult(ModuleVersionIdentifier id, ModuleVersionSelectionReason selectionReason) {
         assert id != null;
         assert selectionReason != null;
diff --git a/subprojects/core-impl/src/main/groovy/org/gradle/api/internal/artifacts/result/DefaultUnresolvedDependencyResult.java b/subprojects/core-impl/src/main/groovy/org/gradle/api/internal/artifacts/result/DefaultUnresolvedDependencyResult.java
index 6b95007..59cbf12 100644
--- a/subprojects/core-impl/src/main/groovy/org/gradle/api/internal/artifacts/result/DefaultUnresolvedDependencyResult.java
+++ b/subprojects/core-impl/src/main/groovy/org/gradle/api/internal/artifacts/result/DefaultUnresolvedDependencyResult.java
@@ -22,9 +22,6 @@ import org.gradle.api.artifacts.result.ResolvedModuleVersionResult;
 import org.gradle.api.artifacts.result.UnresolvedDependencyResult;
 import org.gradle.api.internal.artifacts.ivyservice.ModuleVersionResolveException;
 
-/**
- * by Szczepan Faber, created at: 7/26/12
- */
 public class DefaultUnresolvedDependencyResult extends AbstractDependencyResult implements UnresolvedDependencyResult {
     private final ModuleVersionSelectionReason reason;
     private final ModuleVersionResolveException failure;
diff --git a/subprojects/core-impl/src/main/groovy/org/gradle/api/internal/externalresource/AbstractExternalResource.java b/subprojects/core-impl/src/main/groovy/org/gradle/api/internal/externalresource/AbstractExternalResource.java
index 72610c1..1142f22 100644
--- a/subprojects/core-impl/src/main/groovy/org/gradle/api/internal/externalresource/AbstractExternalResource.java
+++ b/subprojects/core-impl/src/main/groovy/org/gradle/api/internal/externalresource/AbstractExternalResource.java
@@ -16,11 +16,16 @@
 package org.gradle.api.internal.externalresource;
 
 import org.apache.commons.io.IOUtils;
-import org.apache.ivy.plugins.repository.Resource;
+import org.gradle.api.Action;
+import org.gradle.api.Transformer;
 
 import java.io.*;
 
 public abstract class AbstractExternalResource implements ExternalResource {
+    /**
+     * Opens an unbuffered input stream to read the contents of this resource.
+     */
+    protected abstract InputStream openStream() throws IOException;
 
     public void writeTo(File destination) throws IOException {
         FileOutputStream output = new FileOutputStream(destination);
@@ -40,9 +45,22 @@ public abstract class AbstractExternalResource implements ExternalResource {
         }
     }
 
+    public void withContent(Action<? super InputStream> readAction) throws IOException {
+        InputStream input = openStream();
+        try {
+            readAction.execute(input);
+        } finally {
+            input.close();
+        }
+    }
 
-    public Resource clone(String cloneName) {
-        throw new UnsupportedOperationException();
+    public <T> T withContent(Transformer<? extends T, ? super InputStream> readAction) throws IOException {
+        InputStream input = openStream();
+        try {
+            return readAction.transform(input);
+        } finally {
+            input.close();
+        }
     }
 
     public void close() throws IOException {
diff --git a/subprojects/core-impl/src/main/groovy/org/gradle/api/internal/externalresource/DefaultLocallyAvailableExternalResource.java b/subprojects/core-impl/src/main/groovy/org/gradle/api/internal/externalresource/DefaultLocallyAvailableExternalResource.java
new file mode 100644
index 0000000..ec51cc5
--- /dev/null
+++ b/subprojects/core-impl/src/main/groovy/org/gradle/api/internal/externalresource/DefaultLocallyAvailableExternalResource.java
@@ -0,0 +1,48 @@
+/*
+ * Copyright 2012 the original author or authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.gradle.api.internal.externalresource;
+
+import org.gradle.api.internal.externalresource.metadata.ExternalResourceMetaData;
+import org.gradle.internal.resource.local.LocallyAvailableResource;
+import org.gradle.util.hash.HashValue;
+
+public class DefaultLocallyAvailableExternalResource extends LocalFileStandInExternalResource implements LocallyAvailableExternalResource {
+    private final LocallyAvailableResource locallyAvailableResource;
+
+    public DefaultLocallyAvailableExternalResource(String source, LocallyAvailableResource locallyAvailableResource) {
+        this(source, locallyAvailableResource, null);
+    }
+
+    public DefaultLocallyAvailableExternalResource(String source, LocallyAvailableResource locallyAvailableResource, ExternalResourceMetaData metaData) {
+        super(source, locallyAvailableResource.getFile(), metaData);
+        this.locallyAvailableResource = locallyAvailableResource;
+    }
+
+    public LocallyAvailableResource getLocalResource() {
+        return locallyAvailableResource;
+    }
+
+    @Override
+    public long getContentLength() {
+        return locallyAvailableResource.getContentLength();
+    }
+
+    @Override
+    protected HashValue getLocalFileSha1() {
+        return locallyAvailableResource.getSha1();
+    }
+}
diff --git a/subprojects/core-impl/src/main/groovy/org/gradle/api/internal/externalresource/ExternalResource.java b/subprojects/core-impl/src/main/groovy/org/gradle/api/internal/externalresource/ExternalResource.java
index a588007..1e2ce4a 100644
--- a/subprojects/core-impl/src/main/groovy/org/gradle/api/internal/externalresource/ExternalResource.java
+++ b/subprojects/core-impl/src/main/groovy/org/gradle/api/internal/externalresource/ExternalResource.java
@@ -15,24 +15,74 @@
  */
 package org.gradle.api.internal.externalresource;
 
-import org.apache.ivy.plugins.repository.Resource;
-import org.gradle.api.Nullable;
+import org.gradle.api.Action;
+import org.gradle.api.Transformer;
 import org.gradle.api.internal.externalresource.metadata.ExternalResourceMetaData;
 
 import java.io.File;
 import java.io.IOException;
+import java.io.InputStream;
 import java.io.OutputStream;
 
-public interface ExternalResource extends Resource {
+public interface ExternalResource {
+    /**
+     * Get the name of the resource.
+     */
+    public String getName();
+
+    /**
+     * Get the date the resource was last modified
+     *
+     * @return A <code>long</code> value representing the time the file was last modified,
+     *         measured in milliseconds since the epoch (00:00:00 GMT, January 1, 1970), or
+     *         <code>0L</code> if the file does not exist or if an I/O error occurs.
+     */
+    public long getLastModified();
+
+    /**
+     * Get the resource size
+     *
+     * @return a <code>long</code> value representing the size of the resource in bytes.
+     */
+    public long getContentLength();
+
+    /**
+     * Determine if the resource is available.
+     * </p>
+     * Note that this method only checks for availability, not for actual existence.
+     *
+     * @return <code>boolean</code> value indicating if the resource is available.
+     */
+    public boolean exists();
+
+    /**
+     * Is this resource local to this host, i.e. is it on the file system?
+     *
+     * @return <code>boolean</code> value indicating if the resource is local.
+     */
+    public boolean isLocal();
+
+    /**
+     * Copies the contents of this resource to the given file.
+     */
     void writeTo(File destination) throws IOException;
 
     /**
-     * Writes to the given stream. Does not close the stream.
+     * Copies the contents of this resource to the given stream. Does not close the stream.
      */
     void writeTo(OutputStream destination) throws IOException;
 
+    /**
+     * Executes the given action against the contents of this resource.
+     */
+    void withContent(Action<? super InputStream> readAction) throws IOException;
+
+    /**
+     * Executes the given action against the contents of this resource.
+     */
+    <T> T withContent(Transformer<? extends T, ? super InputStream> readAction) throws IOException;
+
     void close() throws IOException;
 
-    @Nullable
     ExternalResourceMetaData getMetaData();
 }
diff --git a/subprojects/core-impl/src/main/groovy/org/gradle/api/internal/externalresource/LocallyAvailableExternalResource.java b/subprojects/core-impl/src/main/groovy/org/gradle/api/internal/externalresource/LocallyAvailableExternalResource.java
index 0b4cb72..0d7e582 100644
--- a/subprojects/core-impl/src/main/groovy/org/gradle/api/internal/externalresource/LocallyAvailableExternalResource.java
+++ b/subprojects/core-impl/src/main/groovy/org/gradle/api/internal/externalresource/LocallyAvailableExternalResource.java
@@ -1,5 +1,5 @@
 /*
- * Copyright 2012 the original author or authors.
+ * Copyright 2013 the original author or authors.
  *
  * Licensed under the Apache License, Version 2.0 (the "License");
  * you may not use this file except in compliance with the License.
@@ -16,30 +16,12 @@
 
 package org.gradle.api.internal.externalresource;
 
-import org.gradle.api.internal.externalresource.local.LocallyAvailableResource;
-import org.gradle.api.internal.externalresource.metadata.ExternalResourceMetaData;
-import org.gradle.util.hash.HashValue;
+import org.gradle.internal.resource.local.LocallyAvailableResource;
 
-public class LocallyAvailableExternalResource extends LocalFileStandInExternalResource {
-
-    private final LocallyAvailableResource locallyAvailableResource;
-
-    public LocallyAvailableExternalResource(String source, LocallyAvailableResource locallyAvailableResource) {
-        this(source, locallyAvailableResource, null);
-    }
-
-    public LocallyAvailableExternalResource(String source, LocallyAvailableResource locallyAvailableResource, ExternalResourceMetaData metaData) {
-        super(source, locallyAvailableResource.getFile(), metaData);
-        this.locallyAvailableResource = locallyAvailableResource;
-    }
-
-    @Override
-    public long getContentLength() {
-        return locallyAvailableResource.getContentLength();
-    }
-
-    @Override
-    protected HashValue getLocalFileSha1() {
-        return locallyAvailableResource.getSha1();
-    }
+/**
+ * Represents an external resource whose meta-data and content is available locally. The content and meta-data may be a copy of some original resource and the original may or may not be a local
+ * resource.
+ */
+public interface LocallyAvailableExternalResource extends ExternalResource {
+    LocallyAvailableResource getLocalResource();
 }
diff --git a/subprojects/core-impl/src/main/groovy/org/gradle/api/internal/externalresource/UrlExternalResource.java b/subprojects/core-impl/src/main/groovy/org/gradle/api/internal/externalresource/UrlExternalResource.java
new file mode 100644
index 0000000..f47a3bf
--- /dev/null
+++ b/subprojects/core-impl/src/main/groovy/org/gradle/api/internal/externalresource/UrlExternalResource.java
@@ -0,0 +1,65 @@
+/*
+ * Copyright 2013 the original author or authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.gradle.api.internal.externalresource;
+
+import org.gradle.api.internal.externalresource.metadata.DefaultExternalResourceMetaData;
+import org.gradle.api.internal.externalresource.metadata.ExternalResourceMetaData;
+
+import java.io.IOException;
+import java.io.InputStream;
+import java.net.URL;
+import java.net.URLConnection;
+
+public class UrlExternalResource extends AbstractExternalResource {
+    private final URL url;
+    private final URLConnection connection;
+    private final DefaultExternalResourceMetaData metaData;
+
+    public UrlExternalResource(URL url) throws IOException {
+        this.url = url;
+        connection = url.openConnection();
+        metaData = new DefaultExternalResourceMetaData(url.toString(), connection.getLastModified(), connection.getContentLength(), null, null);
+    }
+
+    public String getName() {
+        return url.toExternalForm();
+    }
+
+    public ExternalResourceMetaData getMetaData() {
+        return metaData;
+    }
+
+    public boolean isLocal() {
+        return url.getProtocol().equalsIgnoreCase("file");
+    }
+
+    public long getContentLength() {
+        return connection.getContentLength();
+    }
+
+    public long getLastModified() {
+        return connection.getLastModified();
+    }
+
+    public boolean exists() {
+        return true;
+    }
+
+    public InputStream openStream() throws IOException {
+        return connection.getInputStream();
+    }
+}
diff --git a/subprojects/core-impl/src/main/groovy/org/gradle/api/internal/externalresource/ivy/ArtifactAtRepositoryCachedArtifactIndex.java b/subprojects/core-impl/src/main/groovy/org/gradle/api/internal/externalresource/ivy/ArtifactAtRepositoryCachedArtifactIndex.java
index 9f297a4..bf33e52 100644
--- a/subprojects/core-impl/src/main/groovy/org/gradle/api/internal/externalresource/ivy/ArtifactAtRepositoryCachedArtifactIndex.java
+++ b/subprojects/core-impl/src/main/groovy/org/gradle/api/internal/externalresource/ivy/ArtifactAtRepositoryCachedArtifactIndex.java
@@ -16,17 +16,20 @@
 
 package org.gradle.api.internal.externalresource.ivy;
 
+import org.gradle.api.artifacts.ArtifactIdentifier;
+import org.gradle.api.artifacts.ModuleVersionIdentifier;
+import org.gradle.api.internal.artifacts.DefaultArtifactIdentifier;
+import org.gradle.api.internal.artifacts.ModuleVersionIdentifierSerializer;
 import org.gradle.api.internal.artifacts.ivyservice.CacheLockingManager;
 import org.gradle.api.internal.externalresource.cached.CachedArtifact;
 import org.gradle.api.internal.externalresource.cached.CachedArtifactIndex;
 import org.gradle.api.internal.externalresource.cached.DefaultCachedArtifact;
 import org.gradle.internal.TimeProvider;
-import org.gradle.messaging.serialize.DataStreamBackedSerializer;
+import org.gradle.messaging.serialize.Decoder;
+import org.gradle.messaging.serialize.Encoder;
+import org.gradle.messaging.serialize.Serializer;
 
-import java.io.DataInput;
-import java.io.DataOutput;
 import java.io.File;
-import java.io.IOException;
 import java.math.BigInteger;
 
 public class ArtifactAtRepositoryCachedArtifactIndex extends AbstractCachedIndex<ArtifactAtRepositoryKey, CachedArtifact> implements CachedArtifactIndex {
@@ -55,47 +58,51 @@ public class ArtifactAtRepositoryCachedArtifactIndex extends AbstractCachedIndex
         return new DefaultCachedArtifact(timeProvider.getCurrentTime(), descriptorHash);
     }
 
-    private static class ArtifactAtRepositoryKeySerializer extends DataStreamBackedSerializer<ArtifactAtRepositoryKey> {
-        @Override
-        public void write(DataOutput dataOutput, ArtifactAtRepositoryKey value) throws IOException {
-            dataOutput.writeUTF(value.getRepositoryId());
-            dataOutput.writeUTF(value.getArtifactId());
+    private static class ArtifactAtRepositoryKeySerializer implements Serializer<ArtifactAtRepositoryKey> {
+        private final ModuleVersionIdentifierSerializer modIdSerializer = new ModuleVersionIdentifierSerializer();
+
+        public void write(Encoder encoder, ArtifactAtRepositoryKey value) throws Exception {
+            encoder.writeString(value.getRepositoryId());
+            ArtifactIdentifier artifact = value.getArtifactId();
+            modIdSerializer.write(encoder, artifact.getModuleVersionIdentifier());
+            encoder.writeString(artifact.getName());
+            encoder.writeNullableString(artifact.getExtension());
+            encoder.writeNullableString(artifact.getClassifier());
+            encoder.writeNullableString(artifact.getType());
         }
 
-        @Override
-        public ArtifactAtRepositoryKey read(DataInput dataInput) throws IOException {
-            String repositoryId = dataInput.readUTF();
-            String artifactId = dataInput.readUTF();
-            return new ArtifactAtRepositoryKey(repositoryId, artifactId);
+        public ArtifactAtRepositoryKey read(Decoder decoder) throws Exception {
+            String repositoryId = decoder.readString();
+            ModuleVersionIdentifier moduleVersionIdentifier = modIdSerializer.read(decoder);
+            String artifactName = decoder.readString();
+            String extension = decoder.readNullableString();
+            String classifier = decoder.readNullableString();
+            String type = decoder.readNullableString();
+            return new ArtifactAtRepositoryKey(repositoryId, new DefaultArtifactIdentifier(moduleVersionIdentifier, artifactName, type, extension, classifier));
         }
     }
 
-    private static class CachedArtifactSerializer extends DataStreamBackedSerializer<CachedArtifact> {
-        @Override
-        public void write(DataOutput dataOutput, CachedArtifact value) throws IOException {
-            dataOutput.writeBoolean(value.isMissing());
+    private static class CachedArtifactSerializer implements Serializer<CachedArtifact> {
+        public void write(Encoder encoder, CachedArtifact value) throws Exception {
+            encoder.writeBoolean(value.isMissing());
             if (!value.isMissing()) {
-                dataOutput.writeUTF(value.getCachedFile().getPath());
+                encoder.writeString(value.getCachedFile().getPath());
             }
-            dataOutput.writeLong(value.getCachedAt());
+            encoder.writeLong(value.getCachedAt());
             byte[] hash = value.getDescriptorHash().toByteArray();
-            dataOutput.writeInt(hash.length);
-            dataOutput.write(hash);
+            encoder.writeBinary(hash);
         }
 
-        @Override
-        public CachedArtifact read(DataInput dataInput) throws Exception {
-            boolean isMissing = dataInput.readBoolean();
+        public CachedArtifact read(Decoder decoder) throws Exception {
+            boolean isMissing = decoder.readBoolean();
             File file;
             if (!isMissing) {
-                file = new File(dataInput.readUTF());
+                file = new File(decoder.readString());
             } else {
                 file = null;
             }
-            long createTimestamp = dataInput.readLong();
-            int count = dataInput.readInt();
-            byte[] encodedHash = new byte[count];
-            dataInput.readFully(encodedHash);
+            long createTimestamp = decoder.readLong();
+            byte[] encodedHash = decoder.readBinary();
             BigInteger hash = new BigInteger(encodedHash);
             return new DefaultCachedArtifact(file, createTimestamp, hash);
         }
diff --git a/subprojects/core-impl/src/main/groovy/org/gradle/api/internal/externalresource/ivy/ArtifactAtRepositoryKey.java b/subprojects/core-impl/src/main/groovy/org/gradle/api/internal/externalresource/ivy/ArtifactAtRepositoryKey.java
index 760159f..e1cc064 100644
--- a/subprojects/core-impl/src/main/groovy/org/gradle/api/internal/externalresource/ivy/ArtifactAtRepositoryKey.java
+++ b/subprojects/core-impl/src/main/groovy/org/gradle/api/internal/externalresource/ivy/ArtifactAtRepositoryKey.java
@@ -16,37 +16,18 @@
 
 package org.gradle.api.internal.externalresource.ivy;
 
-import org.apache.ivy.core.IvyPatternHelper;
-import org.apache.ivy.core.module.descriptor.Artifact;
-import org.apache.ivy.core.module.descriptor.DefaultArtifact;
-import org.apache.ivy.core.module.id.ArtifactRevisionId;
-import org.gradle.api.internal.artifacts.ivyservice.ivyresolve.ModuleVersionRepository;
+import org.gradle.api.artifacts.ArtifactIdentifier;
 
 public class ArtifactAtRepositoryKey {
     private final String repositoryId;
-    private final String artifactId;
+    private final ArtifactIdentifier artifactId;
 
-    public ArtifactAtRepositoryKey(ModuleVersionRepository repository, ArtifactRevisionId artifactId) {
-        this(repository, getArtifactKey(artifactId));
-    }
-
-    public ArtifactAtRepositoryKey(String repositoryId, String artifactId) {
+    public ArtifactAtRepositoryKey(String repositoryId, ArtifactIdentifier artifactId) {
         this.repositoryId = repositoryId;
         this.artifactId = artifactId;
     }
 
-    private ArtifactAtRepositoryKey(ModuleVersionRepository repository, String artifactPath) {
-        this.repositoryId = repository.getId();
-        this.artifactId = artifactPath;
-    }
-
-    private static String getArtifactKey(ArtifactRevisionId artifactId) {
-        String format = "[organisation]/[module](/[branch])/[revision]/[type]/[artifact](-[classifier])(.[ext])";
-        Artifact dummyArtifact = new DefaultArtifact(artifactId, null, null, false);
-        return IvyPatternHelper.substitute(format, dummyArtifact);
-    }
-
-    public String getArtifactId() {
+    public ArtifactIdentifier getArtifactId() {
         return artifactId;
     }
 
@@ -65,11 +46,11 @@ public class ArtifactAtRepositoryKey {
             return false;
         }
         ArtifactAtRepositoryKey other = (ArtifactAtRepositoryKey) o;
-        return toString().equals(other.toString());
+        return repositoryId.equals(other.repositoryId) && artifactId.equals(other.artifactId);
     }
 
     @Override
     public int hashCode() {
-        return toString().hashCode();
+        return repositoryId.hashCode() ^ artifactId.hashCode();
     }
 }
diff --git a/subprojects/core-impl/src/main/groovy/org/gradle/api/internal/externalresource/local/CompositeLocallyAvailableResourceFinder.java b/subprojects/core-impl/src/main/groovy/org/gradle/api/internal/externalresource/local/CompositeLocallyAvailableResourceFinder.java
index db6f783..4eee4be 100644
--- a/subprojects/core-impl/src/main/groovy/org/gradle/api/internal/externalresource/local/CompositeLocallyAvailableResourceFinder.java
+++ b/subprojects/core-impl/src/main/groovy/org/gradle/api/internal/externalresource/local/CompositeLocallyAvailableResourceFinder.java
@@ -16,6 +16,7 @@
 
 package org.gradle.api.internal.externalresource.local;
 
+import org.gradle.internal.resource.local.LocallyAvailableResource;
 import org.gradle.util.hash.HashValue;
 
 import java.util.LinkedList;
diff --git a/subprojects/core-impl/src/main/groovy/org/gradle/api/internal/externalresource/local/DefaultLocallyAvailableResource.java b/subprojects/core-impl/src/main/groovy/org/gradle/api/internal/externalresource/local/DefaultLocallyAvailableResource.java
deleted file mode 100644
index b6ff3d1..0000000
--- a/subprojects/core-impl/src/main/groovy/org/gradle/api/internal/externalresource/local/DefaultLocallyAvailableResource.java
+++ /dev/null
@@ -1,65 +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.api.internal.externalresource.local;
-
-import org.gradle.util.hash.HashUtil;
-import org.gradle.util.hash.HashValue;
-
-import java.io.File;
-
-public class DefaultLocallyAvailableResource implements LocallyAvailableResource {
-    private final File origin;
-    
-    // Calculated on demand
-    private HashValue sha1;
-    private Long contentLength;
-    private Long lastModified;
-
-    public DefaultLocallyAvailableResource(File origin) {
-        this.origin = origin;
-    }
-
-    public DefaultLocallyAvailableResource(File origin, HashValue sha1) {
-        this(origin);
-        this.sha1 = sha1;
-    }
-
-    public File getFile() {
-        return origin;
-    }
-
-    public HashValue getSha1() {
-        if (sha1 == null) {
-            this.sha1 = HashUtil.sha1(origin);
-        }
-        return sha1;
-    }
-
-    public long getContentLength() {
-        if (contentLength == null) {
-            contentLength = origin.length();
-        }
-        return contentLength;
-    }
-
-    public long getLastModified() {
-        if (lastModified == null) {
-            lastModified = origin.lastModified();
-        }
-        return lastModified;
-    }
-
-}
diff --git a/subprojects/core-impl/src/main/groovy/org/gradle/api/internal/externalresource/local/LazyLocallyAvailableResourceCandidates.java b/subprojects/core-impl/src/main/groovy/org/gradle/api/internal/externalresource/local/LazyLocallyAvailableResourceCandidates.java
index f9a9b69..789d481 100644
--- a/subprojects/core-impl/src/main/groovy/org/gradle/api/internal/externalresource/local/LazyLocallyAvailableResourceCandidates.java
+++ b/subprojects/core-impl/src/main/groovy/org/gradle/api/internal/externalresource/local/LazyLocallyAvailableResourceCandidates.java
@@ -17,6 +17,8 @@
 package org.gradle.api.internal.externalresource.local;
 
 import org.gradle.internal.Factory;
+import org.gradle.internal.resource.local.DefaultLocallyAvailableResource;
+import org.gradle.internal.resource.local.LocallyAvailableResource;
 import org.gradle.util.hash.HashUtil;
 import org.gradle.util.hash.HashValue;
 
diff --git a/subprojects/core-impl/src/main/groovy/org/gradle/api/internal/externalresource/local/LocallyAvailableResource.java b/subprojects/core-impl/src/main/groovy/org/gradle/api/internal/externalresource/local/LocallyAvailableResource.java
deleted file mode 100644
index 5113da1..0000000
--- a/subprojects/core-impl/src/main/groovy/org/gradle/api/internal/externalresource/local/LocallyAvailableResource.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.api.internal.externalresource.local;
-
-import org.gradle.util.hash.HashValue;
-
-import java.io.File;
-
-public interface LocallyAvailableResource {
-
-    File getFile();
-
-    HashValue getSha1();
-
-    long getLastModified();
-
-    long getContentLength();
-}
diff --git a/subprojects/core-impl/src/main/groovy/org/gradle/api/internal/externalresource/local/LocallyAvailableResourceCandidates.java b/subprojects/core-impl/src/main/groovy/org/gradle/api/internal/externalresource/local/LocallyAvailableResourceCandidates.java
index 5694ce0..6d45b8d 100644
--- a/subprojects/core-impl/src/main/groovy/org/gradle/api/internal/externalresource/local/LocallyAvailableResourceCandidates.java
+++ b/subprojects/core-impl/src/main/groovy/org/gradle/api/internal/externalresource/local/LocallyAvailableResourceCandidates.java
@@ -16,6 +16,7 @@
 
 package org.gradle.api.internal.externalresource.local;
 
+import org.gradle.internal.resource.local.LocallyAvailableResource;
 import org.gradle.util.hash.HashValue;
 
 /**
diff --git a/subprojects/core-impl/src/main/groovy/org/gradle/api/internal/externalresource/local/LocallyAvailableResourceFinder.java b/subprojects/core-impl/src/main/groovy/org/gradle/api/internal/externalresource/local/LocallyAvailableResourceFinder.java
index 610c33e..5c84337 100644
--- a/subprojects/core-impl/src/main/groovy/org/gradle/api/internal/externalresource/local/LocallyAvailableResourceFinder.java
+++ b/subprojects/core-impl/src/main/groovy/org/gradle/api/internal/externalresource/local/LocallyAvailableResourceFinder.java
@@ -21,7 +21,7 @@ package org.gradle.api.internal.externalresource.local;
  *
  * This is different to our caching in that we know very little about locally available resources, other than their
  * binary content. If we can determine the sha1 value of an external resource, we can search the local system to see
- * if a copy can be found (e.g. the local maven cache).
+ * if a copy can be found (e.g. the local Maven cache).
  *
  * @param <C> The type of the criterion object used to find candidates
  */
diff --git a/subprojects/core-impl/src/main/groovy/org/gradle/api/internal/externalresource/local/LocallyAvailableResourceFinderSearchableFileStoreAdapter.java b/subprojects/core-impl/src/main/groovy/org/gradle/api/internal/externalresource/local/LocallyAvailableResourceFinderSearchableFileStoreAdapter.java
index 436c373..6047525 100644
--- a/subprojects/core-impl/src/main/groovy/org/gradle/api/internal/externalresource/local/LocallyAvailableResourceFinderSearchableFileStoreAdapter.java
+++ b/subprojects/core-impl/src/main/groovy/org/gradle/api/internal/externalresource/local/LocallyAvailableResourceFinderSearchableFileStoreAdapter.java
@@ -17,9 +17,9 @@
 package org.gradle.api.internal.externalresource.local;
 
 import org.gradle.api.Transformer;
-import org.gradle.api.internal.filestore.FileStoreEntry;
-import org.gradle.api.internal.filestore.FileStoreSearcher;
+import org.gradle.internal.filestore.FileStoreSearcher;
 import org.gradle.internal.Factory;
+import org.gradle.internal.resource.local.LocallyAvailableResource;
 import org.gradle.util.CollectionUtils;
 
 import java.io.File;
@@ -38,9 +38,9 @@ public class LocallyAvailableResourceFinderSearchableFileStoreAdapter<C> extends
             public Factory<List<File>> transform(final C criterion) {
                 return new Factory<List<File>>() {
                     public List<File> create() {
-                        Set<? extends FileStoreEntry> entries = fileStore.search(criterion);
-                        return CollectionUtils.collect(entries, new ArrayList<File>(entries.size()), new Transformer<File, FileStoreEntry>() {
-                            public File transform(FileStoreEntry original) {
+                        Set<? extends LocallyAvailableResource> entries = fileStore.search(criterion);
+                        return CollectionUtils.collect(entries, new ArrayList<File>(entries.size()), new Transformer<File, LocallyAvailableResource>() {
+                            public File transform(LocallyAvailableResource original) {
                                 return original.getFile();
                             }
                         });
diff --git a/subprojects/core-impl/src/main/groovy/org/gradle/api/internal/externalresource/local/ivy/LocallyAvailableResourceFinderFactory.java b/subprojects/core-impl/src/main/groovy/org/gradle/api/internal/externalresource/local/ivy/LocallyAvailableResourceFinderFactory.java
index 3cec598..3905eb0 100644
--- a/subprojects/core-impl/src/main/groovy/org/gradle/api/internal/externalresource/local/ivy/LocallyAvailableResourceFinderFactory.java
+++ b/subprojects/core-impl/src/main/groovy/org/gradle/api/internal/externalresource/local/ivy/LocallyAvailableResourceFinderFactory.java
@@ -23,8 +23,9 @@ import org.gradle.api.internal.artifacts.repositories.resolver.IvyResourcePatter
 import org.gradle.api.internal.artifacts.repositories.resolver.M2ResourcePattern;
 import org.gradle.api.internal.artifacts.repositories.resolver.ResourcePattern;
 import org.gradle.api.internal.externalresource.local.*;
-import org.gradle.api.internal.filestore.FileStoreSearcher;
+import org.gradle.internal.filestore.FileStoreSearcher;
 import org.gradle.internal.Factory;
+import org.gradle.internal.resource.local.LocallyAvailableResource;
 import org.gradle.util.hash.HashValue;
 import org.slf4j.Logger;
 import org.slf4j.LoggerFactory;
@@ -55,6 +56,12 @@ public class LocallyAvailableResourceFinderFactory implements Factory<LocallyAva
         // The current filestore
         finders.add(new LocallyAvailableResourceFinderSearchableFileStoreAdapter<ArtifactRevisionId>(fileStore));
 
+        // 1.5
+        addForPattern(finders, "artifacts-24", "filestore/[organisation]/[module](/[branch])/[revision]/[type]/*/[artifact]-[revision](-[classifier])(.[ext])");
+
+        // 1.4
+        addForPattern(finders, "artifacts-23", "filestore/[organisation]/[module](/[branch])/[revision]/[type]/*/[artifact]-[revision](-[classifier])(.[ext])");
+
         // 1.3
         addForPattern(finders, "artifacts-15", "filestore/[organisation]/[module](/[branch])/[revision]/[type]/*/[artifact]-[revision](-[classifier])(.[ext])");
 
@@ -110,7 +117,7 @@ public class LocallyAvailableResourceFinderFactory implements Factory<LocallyAva
         public LocallyAvailableResourceCandidates findCandidates(ArtifactRevisionId criterion) {
             if(!logged){
                 LOGGER.warn("Unable to locate local Maven repository.");
-                LOGGER.debug("Problems while locating local maven repository.", ex);
+                LOGGER.debug("Problems while locating local Maven repository.", ex);
                 logged = true;
             }
             return new LocallyAvailableResourceCandidates() {
diff --git a/subprojects/core-impl/src/main/groovy/org/gradle/api/internal/externalresource/transfer/DefaultCacheAwareExternalResourceAccessor.java b/subprojects/core-impl/src/main/groovy/org/gradle/api/internal/externalresource/transfer/DefaultCacheAwareExternalResourceAccessor.java
index 97c97b0..2cfb362 100644
--- a/subprojects/core-impl/src/main/groovy/org/gradle/api/internal/externalresource/transfer/DefaultCacheAwareExternalResourceAccessor.java
+++ b/subprojects/core-impl/src/main/groovy/org/gradle/api/internal/externalresource/transfer/DefaultCacheAwareExternalResourceAccessor.java
@@ -17,12 +17,12 @@
 package org.gradle.api.internal.externalresource.transfer;
 
 import org.gradle.api.Nullable;
+import org.gradle.api.internal.externalresource.DefaultLocallyAvailableExternalResource;
 import org.gradle.api.internal.externalresource.ExternalResource;
-import org.gradle.api.internal.externalresource.LocallyAvailableExternalResource;
 import org.gradle.api.internal.externalresource.cached.CachedExternalResource;
 import org.gradle.api.internal.externalresource.cached.CachedExternalResourceAdapter;
 import org.gradle.api.internal.externalresource.cached.CachedExternalResourceIndex;
-import org.gradle.api.internal.externalresource.local.LocallyAvailableResource;
+import org.gradle.internal.resource.local.LocallyAvailableResource;
 import org.gradle.api.internal.externalresource.local.LocallyAvailableResourceCandidates;
 import org.gradle.api.internal.externalresource.metadata.ExternalResourceMetaData;
 import org.gradle.api.internal.externalresource.metadata.ExternalResourceMetaDataCompare;
@@ -94,7 +94,7 @@ public class DefaultCacheAwareExternalResourceAccessor implements CacheAwareExte
                 LocallyAvailableResource local = localCandidates.findByHashValue(remoteChecksum);
                 if (local != null) {
                     LOGGER.info("Found locally available resource with matching checksum: [{}, {}]", location, local.getFile());
-                    return new LocallyAvailableExternalResource(location, local, remoteMetaData);
+                    return new DefaultLocallyAvailableExternalResource(location, local, remoteMetaData);
                 }
             }
         }
diff --git a/subprojects/core-impl/src/main/groovy/org/gradle/api/internal/externalresource/transfer/ProgressLoggingExternalResourceAccessor.java b/subprojects/core-impl/src/main/groovy/org/gradle/api/internal/externalresource/transfer/ProgressLoggingExternalResourceAccessor.java
index 32182a8..ff1b095 100644
--- a/subprojects/core-impl/src/main/groovy/org/gradle/api/internal/externalresource/transfer/ProgressLoggingExternalResourceAccessor.java
+++ b/subprojects/core-impl/src/main/groovy/org/gradle/api/internal/externalresource/transfer/ProgressLoggingExternalResourceAccessor.java
@@ -16,8 +16,9 @@
 
 package org.gradle.api.internal.externalresource.transfer;
 
-import org.apache.ivy.plugins.repository.Resource;
+import org.gradle.api.Action;
 import org.gradle.api.Nullable;
+import org.gradle.api.Transformer;
 import org.gradle.api.internal.externalresource.ExternalResource;
 import org.gradle.api.internal.externalresource.metadata.ExternalResourceMetaData;
 import org.gradle.logging.ProgressLoggerFactory;
@@ -81,8 +82,12 @@ public class ProgressLoggingExternalResourceAccessor extends AbstractProgressLog
             }
         }
 
-        public Resource clone(String cloneName) {
-            return resource.clone(cloneName);
+        public void withContent(Action<? super InputStream> readAction) throws IOException {
+            resource.withContent(readAction);
+        }
+
+        public <T> T withContent(Transformer<? extends T, ? super InputStream> readAction) throws IOException {
+            return resource.withContent(readAction);
         }
 
         public void close() throws IOException {
@@ -114,10 +119,6 @@ public class ProgressLoggingExternalResourceAccessor extends AbstractProgressLog
             return resource.isLocal();
         }
 
-        public InputStream openStream() throws IOException {
-            return resource.openStream();
-        }
-
         public String toString(){
             return resource.toString();
         }
diff --git a/subprojects/core-impl/src/main/groovy/org/gradle/api/internal/externalresource/transport/file/FileResourceConnector.java b/subprojects/core-impl/src/main/groovy/org/gradle/api/internal/externalresource/transport/file/FileResourceConnector.java
index c375699..94e696a 100644
--- a/subprojects/core-impl/src/main/groovy/org/gradle/api/internal/externalresource/transport/file/FileResourceConnector.java
+++ b/subprojects/core-impl/src/main/groovy/org/gradle/api/internal/externalresource/transport/file/FileResourceConnector.java
@@ -16,18 +16,16 @@
 package org.gradle.api.internal.externalresource.transport.file;
 
 import org.apache.commons.io.IOUtils;
+import org.gradle.api.internal.externalresource.DefaultLocallyAvailableExternalResource;
 import org.gradle.api.internal.externalresource.ExternalResource;
-import org.gradle.api.internal.externalresource.LocallyAvailableExternalResource;
-import org.gradle.api.internal.externalresource.local.DefaultLocallyAvailableResource;
 import org.gradle.api.internal.externalresource.metadata.ExternalResourceMetaData;
 import org.gradle.api.internal.externalresource.transfer.ExternalResourceAccessor;
 import org.gradle.api.internal.externalresource.transfer.ExternalResourceLister;
 import org.gradle.api.internal.externalresource.transfer.ExternalResourceUploader;
 import org.gradle.internal.Factory;
+import org.gradle.internal.resource.local.DefaultLocallyAvailableResource;
 import org.gradle.util.GFileUtils;
 import org.gradle.util.hash.HashValue;
-import org.slf4j.Logger;
-import org.slf4j.LoggerFactory;
 
 import java.io.File;
 import java.io.FileOutputStream;
@@ -37,9 +35,6 @@ import java.util.ArrayList;
 import java.util.List;
 
 public class FileResourceConnector implements ExternalResourceLister, ExternalResourceAccessor, ExternalResourceUploader {
-
-    private static final Logger LOGGER = LoggerFactory.getLogger(FileResourceConnector.class);
-
     public List<String> list(String parent) throws IOException {
         File dir = getFile(parent);
         if (dir.exists() && dir.isDirectory()) {
@@ -81,7 +76,7 @@ public class FileResourceConnector implements ExternalResourceLister, ExternalRe
         if (!localFile.exists()) {
             return null;
         }
-        return new LocallyAvailableExternalResource(location, new DefaultLocallyAvailableResource(localFile));
+        return new DefaultLocallyAvailableExternalResource(location, new DefaultLocallyAvailableResource(localFile));
     }
 
     public ExternalResourceMetaData getMetaData(String location) throws IOException {
diff --git a/subprojects/core-impl/src/main/groovy/org/gradle/api/internal/externalresource/transport/file/FileTransport.java b/subprojects/core-impl/src/main/groovy/org/gradle/api/internal/externalresource/transport/file/FileTransport.java
index 652e5d6..240e7d4 100644
--- a/subprojects/core-impl/src/main/groovy/org/gradle/api/internal/externalresource/transport/file/FileTransport.java
+++ b/subprojects/core-impl/src/main/groovy/org/gradle/api/internal/externalresource/transport/file/FileTransport.java
@@ -15,8 +15,8 @@
  */
 package org.gradle.api.internal.externalresource.transport.file;
 
-import org.apache.ivy.core.cache.RepositoryCacheManager;
 import org.gradle.api.Nullable;
+import org.gradle.api.internal.artifacts.repositories.cachemanager.RepositoryArtifactCache;
 import org.gradle.api.internal.artifacts.repositories.resolver.ExternalResourceResolver;
 import org.gradle.api.internal.artifacts.repositories.transport.RepositoryTransport;
 import org.gradle.api.internal.externalresource.ExternalResource;
@@ -32,10 +32,10 @@ import java.net.URI;
 
 public class FileTransport implements RepositoryTransport {
     private final String name;
-    private final RepositoryCacheManager repositoryCacheManager;
+    private final RepositoryArtifactCache repositoryCacheManager;
     private final ExternalResourceRepository repository;
 
-    public FileTransport(String name, RepositoryCacheManager repositoryCacheManager, TemporaryFileProvider temporaryFileProvider) {
+    public FileTransport(String name, RepositoryArtifactCache repositoryCacheManager, TemporaryFileProvider temporaryFileProvider) {
         this.name = name;
         this.repositoryCacheManager = repositoryCacheManager;
         repository = createRepository(temporaryFileProvider);
diff --git a/subprojects/core-impl/src/main/groovy/org/gradle/api/internal/externalresource/transport/http/ApacheDirectoryListingParser.java b/subprojects/core-impl/src/main/groovy/org/gradle/api/internal/externalresource/transport/http/ApacheDirectoryListingParser.java
index 7762d60..437d14e 100644
--- a/subprojects/core-impl/src/main/groovy/org/gradle/api/internal/externalresource/transport/http/ApacheDirectoryListingParser.java
+++ b/subprojects/core-impl/src/main/groovy/org/gradle/api/internal/externalresource/transport/http/ApacheDirectoryListingParser.java
@@ -25,8 +25,7 @@ import org.xml.sax.InputSource;
 import org.xml.sax.SAXException;
 import org.xml.sax.helpers.DefaultHandler;
 
-import java.io.IOException;
-import java.io.StringReader;
+import java.io.*;
 import java.net.MalformedURLException;
 import java.net.URI;
 import java.net.URISyntaxException;
@@ -40,14 +39,14 @@ public class ApacheDirectoryListingParser {
     public ApacheDirectoryListingParser() {
     }
 
-    public List<URI> parse(URI baseURI, byte[] content, String contentType) throws Exception {
+    public List<URI> parse(URI baseURI, InputStream content, String contentType) throws Exception {
         baseURI = addTrailingSlashes(baseURI);
         if (contentType == null || !contentType.startsWith("text/html")) {
             throw new ResourceException(String.format("Unsupported ContentType %s for DirectoryListing", contentType));
         }
         String contentEncoding = contentType.contains("charset=") ? contentType.substring(contentType.indexOf('=') + 1) : "utf-8";
-        final String htmlText = new String(content, contentEncoding);
-        final InputSource inputSource = new InputSource(new StringReader(htmlText));
+        final Reader htmlText = new InputStreamReader(content, contentEncoding);
+        final InputSource inputSource = new InputSource(htmlText);
         final SAXParser htmlParser = new SAXParser();
         final AnchorListerHandler anchorListerHandler = new AnchorListerHandler();
         htmlParser.setContentHandler(anchorListerHandler);
diff --git a/subprojects/core-impl/src/main/groovy/org/gradle/api/internal/externalresource/transport/http/HttpResourceLister.java b/subprojects/core-impl/src/main/groovy/org/gradle/api/internal/externalresource/transport/http/HttpResourceLister.java
index 5905481..ad5179d 100644
--- a/subprojects/core-impl/src/main/groovy/org/gradle/api/internal/externalresource/transport/http/HttpResourceLister.java
+++ b/subprojects/core-impl/src/main/groovy/org/gradle/api/internal/externalresource/transport/http/HttpResourceLister.java
@@ -16,12 +16,12 @@
 
 package org.gradle.api.internal.externalresource.transport.http;
 
-import org.gradle.api.internal.externalresource.ExternalResource;
+import org.gradle.api.Transformer;
 import org.gradle.api.internal.externalresource.transfer.ExternalResourceLister;
 import org.gradle.api.internal.resource.ResourceException;
 
-import java.io.ByteArrayOutputStream;
 import java.io.IOException;
+import java.io.InputStream;
 import java.net.URI;
 import java.net.URISyntaxException;
 import java.util.ArrayList;
@@ -35,7 +35,7 @@ public class HttpResourceLister implements ExternalResourceLister {
     }
 
     public List<String> list(String parent) throws IOException {
-        URI baseURI;
+        final URI baseURI;
         try {
             baseURI = new URI(parent);
         } catch (URISyntaxException ex) {
@@ -46,15 +46,18 @@ public class HttpResourceLister implements ExternalResourceLister {
             return null;
         }
         try {
-            byte[] resourceContent = loadResourceContent(resource);
-            String contentType = resource.getContentType();
-            ApacheDirectoryListingParser directoryListingParser = new ApacheDirectoryListingParser();
-            try {
-                List<URI> uris = directoryListingParser.parse(baseURI, resourceContent, contentType);
-                return convertToStringList(uris);
-            } catch (Exception e) {
-                throw new ResourceException("Unable to parse Http directory listing", e);
-            }
+            return resource.withContent(new Transformer<List<String>, InputStream>() {
+                public List<String> transform(InputStream inputStream) {
+                    String contentType = resource.getContentType();
+                    ApacheDirectoryListingParser directoryListingParser = new ApacheDirectoryListingParser();
+                    try {
+                        List<URI> uris = directoryListingParser.parse(baseURI, inputStream, contentType);
+                        return convertToStringList(uris);
+                    } catch (Exception e) {
+                        throw new ResourceException("Unable to parse Http directory listing", e);
+                    }
+                }
+            });
         } finally {
             resource.close();
         }
@@ -67,10 +70,4 @@ public class HttpResourceLister implements ExternalResourceLister {
         }
         return ret;
     }
-
-    private byte[] loadResourceContent(ExternalResource resource) throws IOException {
-        ByteArrayOutputStream outputStream = new ByteArrayOutputStream();
-        resource.writeTo(outputStream);
-        return outputStream.toByteArray();
-    }
 }
diff --git a/subprojects/core-impl/src/main/groovy/org/gradle/api/internal/externalresource/transport/http/HttpTransport.java b/subprojects/core-impl/src/main/groovy/org/gradle/api/internal/externalresource/transport/http/HttpTransport.java
index c23ea02..e8c10d2 100644
--- a/subprojects/core-impl/src/main/groovy/org/gradle/api/internal/externalresource/transport/http/HttpTransport.java
+++ b/subprojects/core-impl/src/main/groovy/org/gradle/api/internal/externalresource/transport/http/HttpTransport.java
@@ -15,8 +15,8 @@
  */
 package org.gradle.api.internal.externalresource.transport.http;
 
-import org.apache.ivy.core.cache.RepositoryCacheManager;
 import org.gradle.api.artifacts.repositories.PasswordCredentials;
+import org.gradle.api.internal.artifacts.repositories.cachemanager.RepositoryArtifactCache;
 import org.gradle.api.internal.artifacts.repositories.resolver.ExternalResourceResolver;
 import org.gradle.api.internal.artifacts.repositories.transport.RepositoryTransport;
 import org.gradle.api.internal.externalresource.cached.CachedExternalResourceIndex;
@@ -32,10 +32,10 @@ import java.net.URI;
 
 public class HttpTransport implements RepositoryTransport {
     private final String name;
-    private final RepositoryCacheManager repositoryCacheManager;
+    private final RepositoryArtifactCache repositoryCacheManager;
     private final ExternalResourceRepository repository;
 
-    public HttpTransport(String name, PasswordCredentials credentials, RepositoryCacheManager repositoryCacheManager,
+    public HttpTransport(String name, PasswordCredentials credentials, RepositoryArtifactCache repositoryCacheManager,
                          ProgressLoggerFactory progressLoggerFactory, TemporaryFileProvider temporaryFileProvider,
                          CachedExternalResourceIndex<String> cachedExternalResourceIndex) {
         this.name = name;
diff --git a/subprojects/core-impl/src/main/groovy/org/gradle/api/internal/notations/ClientModuleNotationParserFactory.java b/subprojects/core-impl/src/main/groovy/org/gradle/api/internal/notations/ClientModuleNotationParserFactory.java
index a4451a1..60382b4 100644
--- a/subprojects/core-impl/src/main/groovy/org/gradle/api/internal/notations/ClientModuleNotationParserFactory.java
+++ b/subprojects/core-impl/src/main/groovy/org/gradle/api/internal/notations/ClientModuleNotationParserFactory.java
@@ -21,9 +21,6 @@ import org.gradle.api.internal.notations.api.NotationParser;
 import org.gradle.internal.Factory;
 import org.gradle.internal.reflect.Instantiator;
 
-/**
- * @author Hans Dockter
- */
 public class ClientModuleNotationParserFactory implements Factory<NotationParser<ClientModule>> {
 
     private final Instantiator instantiator;
diff --git a/subprojects/core-impl/src/main/groovy/org/gradle/api/internal/notations/DependencyMapNotationParser.java b/subprojects/core-impl/src/main/groovy/org/gradle/api/internal/notations/DependencyMapNotationParser.java
index c15e792..f6f5b72 100644
--- a/subprojects/core-impl/src/main/groovy/org/gradle/api/internal/notations/DependencyMapNotationParser.java
+++ b/subprojects/core-impl/src/main/groovy/org/gradle/api/internal/notations/DependencyMapNotationParser.java
@@ -16,17 +16,14 @@
 package org.gradle.api.internal.notations;
 
 import org.gradle.api.artifacts.ExternalDependency;
-import org.gradle.internal.reflect.Instantiator;
 import org.gradle.api.internal.artifacts.dsl.dependencies.ModuleFactoryHelper;
 import org.gradle.api.internal.notations.parsers.MapKey;
 import org.gradle.api.internal.notations.parsers.MapNotationParser;
 import org.gradle.api.tasks.Optional;
+import org.gradle.internal.reflect.Instantiator;
 
 import java.util.Collection;
 
-/**
- * @author Hans Dockter
- */
 public class DependencyMapNotationParser<T extends ExternalDependency> extends MapNotationParser<T> {
 
     private final Instantiator instantiator;
diff --git a/subprojects/core-impl/src/main/groovy/org/gradle/api/internal/notations/DependencyNotationParser.java b/subprojects/core-impl/src/main/groovy/org/gradle/api/internal/notations/DependencyNotationParser.java
index 3f019dd..dcf7572 100644
--- a/subprojects/core-impl/src/main/groovy/org/gradle/api/internal/notations/DependencyNotationParser.java
+++ b/subprojects/core-impl/src/main/groovy/org/gradle/api/internal/notations/DependencyNotationParser.java
@@ -22,9 +22,6 @@ import org.gradle.api.internal.notations.api.NotationParser;
 
 import java.util.Collection;
 
-/**
- * by Szczepan Faber, created at: 11/8/11
- */
 public class DependencyNotationParser implements NotationParser<Dependency> {
 
     private final NotationParser<Dependency> delegate;
diff --git a/subprojects/core-impl/src/main/groovy/org/gradle/api/internal/notations/DependencyProjectNotationParser.java b/subprojects/core-impl/src/main/groovy/org/gradle/api/internal/notations/DependencyProjectNotationParser.java
index f4e0cd8..4e0fd13 100644
--- a/subprojects/core-impl/src/main/groovy/org/gradle/api/internal/notations/DependencyProjectNotationParser.java
+++ b/subprojects/core-impl/src/main/groovy/org/gradle/api/internal/notations/DependencyProjectNotationParser.java
@@ -23,9 +23,6 @@ import org.gradle.api.internal.notations.parsers.TypedNotationParser;
 
 import java.util.Collection;
 
-/**
- * by Szczepan Faber, created at: 11/10/11
- */
 public class DependencyProjectNotationParser extends TypedNotationParser<Project, ProjectDependency> {
 
     private final DefaultProjectDependencyFactory factory;
diff --git a/subprojects/core-impl/src/main/groovy/org/gradle/api/internal/notations/DependencyStringNotationParser.java b/subprojects/core-impl/src/main/groovy/org/gradle/api/internal/notations/DependencyStringNotationParser.java
index 45f2f5e..c8d100e 100644
--- a/subprojects/core-impl/src/main/groovy/org/gradle/api/internal/notations/DependencyStringNotationParser.java
+++ b/subprojects/core-impl/src/main/groovy/org/gradle/api/internal/notations/DependencyStringNotationParser.java
@@ -28,9 +28,6 @@ import java.util.Collection;
 import java.util.regex.Matcher;
 import java.util.regex.Pattern;
 
-/**
- * by Szczepan Faber, created at: 11/10/11
- */
 public class DependencyStringNotationParser<T extends ExternalDependency> extends TypedNotationParser<CharSequence, T> {
 
     private final Instantiator instantiator;
diff --git a/subprojects/core-impl/src/main/groovy/org/gradle/api/internal/notations/ProjectDependencyFactory.java b/subprojects/core-impl/src/main/groovy/org/gradle/api/internal/notations/ProjectDependencyFactory.java
index 9995133..687af32 100644
--- a/subprojects/core-impl/src/main/groovy/org/gradle/api/internal/notations/ProjectDependencyFactory.java
+++ b/subprojects/core-impl/src/main/groovy/org/gradle/api/internal/notations/ProjectDependencyFactory.java
@@ -25,9 +25,6 @@ import org.gradle.api.tasks.Optional;
 import java.util.Collection;
 import java.util.Map;
 
-/**
- * @author Hans Dockter
- */
 public class ProjectDependencyFactory {
     private final DefaultProjectDependencyFactory factory;
 
diff --git a/subprojects/core-impl/src/test/groovy/org/gradle/api/artifacts/ArtifactsTestUtils.java b/subprojects/core-impl/src/test/groovy/org/gradle/api/artifacts/ArtifactsTestUtils.java
deleted file mode 100644
index be631ac..0000000
--- a/subprojects/core-impl/src/test/groovy/org/gradle/api/artifacts/ArtifactsTestUtils.java
+++ /dev/null
@@ -1,62 +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.api.artifacts;
-
-import org.apache.ivy.core.module.descriptor.Artifact;
-import org.gradle.api.internal.artifacts.DefaultModuleVersionIdentifier;
-import org.gradle.api.internal.artifacts.DefaultResolvedArtifact;
-import org.gradle.internal.Factory;
-import org.jmock.Expectations;
-import org.jmock.Mockery;
-
-import java.io.File;
-import java.util.Collections;
-
-public class ArtifactsTestUtils {
-    
-    public static DefaultResolvedArtifact createResolvedArtifact(final Mockery context, final String name, final String type, final String extension, final File file) {
-        final Artifact artifactStub = context.mock(Artifact.class, "artifact" + name);
-        context.checking(new Expectations() {{
-            allowing(artifactStub).getName();
-            will(returnValue(name));
-            allowing(artifactStub).getType();
-            will(returnValue(type));
-            allowing(artifactStub).getExt();
-            will(returnValue(extension));
-            allowing(artifactStub).getExtraAttributes();
-            will(returnValue(Collections.emptyMap()));
-            allowing(artifactStub).getQualifiedExtraAttributes();
-            will(returnValue(Collections.emptyMap()));
-            allowing(artifactStub).getExtraAttribute(with(org.hamcrest.Matchers.notNullValue(String.class)));
-            will(returnValue(null));
-        }});
-        final Factory artifactSource = context.mock(Factory.class);
-        context.checking(new Expectations() {{
-            allowing(artifactSource).create();
-            will(returnValue(file));
-        }});
-        final ResolvedDependency resolvedDependency = context.mock(ResolvedDependency.class);
-        final ResolvedModuleVersion version = context.mock(ResolvedModuleVersion.class);
-        context.checking(new Expectations() {{
-            allowing(resolvedDependency).getModule();
-            will(returnValue(version));
-            allowing(version).getId();
-            will(returnValue(new DefaultModuleVersionIdentifier("group", name, "1.2")));
-        }});
-        return new DefaultResolvedArtifact(resolvedDependency, artifactStub, artifactSource);
-    }
-    
-}
\ No newline at end of file
diff --git a/subprojects/core-impl/src/test/groovy/org/gradle/api/internal/artifacts/DefaultArtifactIdentifierTest.groovy b/subprojects/core-impl/src/test/groovy/org/gradle/api/internal/artifacts/DefaultArtifactIdentifierTest.groovy
new file mode 100644
index 0000000..61dbe88
--- /dev/null
+++ b/subprojects/core-impl/src/test/groovy/org/gradle/api/internal/artifacts/DefaultArtifactIdentifierTest.groovy
@@ -0,0 +1,46 @@
+/*
+ * Copyright 2013 the original author or authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY 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 spock.lang.Specification
+
+import static org.gradle.api.internal.artifacts.DefaultModuleVersionIdentifier.newId
+import static org.gradle.util.Matchers.strictlyEqual
+import static org.hamcrest.MatcherAssert.assertThat
+
+class DefaultArtifactIdentifierTest extends Specification {
+
+    def "equals"() {
+        def base = new DefaultArtifactIdentifier(newId("org", "lib", "1.0"), "someArtifact", "jar", "jar", "")
+        def same = new DefaultArtifactIdentifier(newId("org", "lib", "1.0"), "someArtifact", "jar", "jar", "")
+
+        def badId = new DefaultArtifactIdentifier(newId("org", "lib", "2.0"), "someArtifact", "jar", "jar", "")
+        def badName = new DefaultArtifactIdentifier(newId("org", "lib", "1.0"), "foo", "jar", "jar", "")
+        def badType = new DefaultArtifactIdentifier(newId("org", "lib", "1.0"), "someArtifact", "bar", "jar", "")
+        def badExtension = new DefaultArtifactIdentifier(newId("org", "lib", "1.0"), "someArtifact", "jar", "xxx", "")
+        def badClassifier = new DefaultArtifactIdentifier(newId("org", "lib", "1.0"), "someArtifact", "jar", "jar", "sources")
+
+        expect:
+        assertThat(base, strictlyEqual(same))
+
+        base != badId
+        base != badName
+        base != badType
+        base != badExtension
+        base != badClassifier
+    }
+}
\ No newline at end of file
diff --git a/subprojects/core-impl/src/test/groovy/org/gradle/api/internal/artifacts/DefaultDependencyManagementServicesTest.groovy b/subprojects/core-impl/src/test/groovy/org/gradle/api/internal/artifacts/DefaultDependencyManagementServicesTest.groovy
index 9c33caf..513d7cc 100644
--- a/subprojects/core-impl/src/test/groovy/org/gradle/api/internal/artifacts/DefaultDependencyManagementServicesTest.groovy
+++ b/subprojects/core-impl/src/test/groovy/org/gradle/api/internal/artifacts/DefaultDependencyManagementServicesTest.groovy
@@ -67,6 +67,7 @@ class DefaultDependencyManagementServicesTest extends Specification {
         _ * parent.get(TimeProvider) >> Mock(TimeProvider)
         _ * parent.get(TemporaryFileProvider) >> Mock(TemporaryFileProvider)
         _ * parent.get(ProjectAccessListener) >> Mock(ProjectAccessListener)
+        _ * parent.get(FileResolver) >> Stub(FileResolver)
     }
 
     private CacheRepository initCacheRepository() {
diff --git a/subprojects/core-impl/src/test/groovy/org/gradle/api/internal/artifacts/DefaultModuleVersionSelectorTest.groovy b/subprojects/core-impl/src/test/groovy/org/gradle/api/internal/artifacts/DefaultModuleVersionSelectorTest.groovy
index bf1032d..09e502e 100644
--- a/subprojects/core-impl/src/test/groovy/org/gradle/api/internal/artifacts/DefaultModuleVersionSelectorTest.groovy
+++ b/subprojects/core-impl/src/test/groovy/org/gradle/api/internal/artifacts/DefaultModuleVersionSelectorTest.groovy
@@ -16,14 +16,12 @@
 
 package org.gradle.api.internal.artifacts
 
+import org.apache.ivy.core.module.id.ModuleRevisionId
 import spock.lang.Specification
 
 import static org.gradle.api.internal.artifacts.DefaultModuleVersionIdentifier.newId
 import static org.gradle.api.internal.artifacts.DefaultModuleVersionSelector.newSelector
 
-/**
- * by Szczepan Faber, created at: 2/11/13
- */
 class DefaultModuleVersionSelectorTest extends Specification {
 
     def "equality"() {
@@ -56,4 +54,16 @@ class DefaultModuleVersionSelectorTest extends Specification {
         !selector.matchesStrictly(differentName)
         !selector.matchesStrictly(differentVersion)
     }
+
+    def "construct from ModuleRevisionId"() {
+        def module = ModuleRevisionId.newInstance("group", "name", "version")
+        def selector = newSelector(module)
+
+        expect:
+        with(selector) {
+            group == "group"
+            name == "name"
+            version == "version"
+        }
+    }
 }
diff --git a/subprojects/core-impl/src/test/groovy/org/gradle/api/internal/artifacts/DefaultResolvedArtifactTest.groovy b/subprojects/core-impl/src/test/groovy/org/gradle/api/internal/artifacts/DefaultResolvedArtifactTest.groovy
index eb7aba8..b1cb0cc 100644
--- a/subprojects/core-impl/src/test/groovy/org/gradle/api/internal/artifacts/DefaultResolvedArtifactTest.groovy
+++ b/subprojects/core-impl/src/test/groovy/org/gradle/api/internal/artifacts/DefaultResolvedArtifactTest.groovy
@@ -16,7 +16,6 @@
 package org.gradle.api.internal.artifacts
 
 import org.apache.ivy.core.module.descriptor.Artifact
-import org.gradle.api.artifacts.ResolvedDependency
 import org.gradle.api.artifacts.ResolvedModuleVersion
 import org.gradle.internal.Factory
 import org.gradle.util.Matchers
@@ -27,29 +26,29 @@ class DefaultResolvedArtifactTest extends Specification {
 
     def "uses extended attributes to determine classifier"() {
         Artifact ivyArtifact = ivyArtifact("name", "type", "ext", ['m:classifier': 'classifier'])
-        ResolvedDependency dependency = Mock()
-        def artifact = new DefaultResolvedArtifact(dependency, ivyArtifact, artifactSource)
+        ResolvedModuleVersion owner = Mock()
+        def artifact = new DefaultResolvedArtifact(owner, {} as Factory, ivyArtifact, artifactSource, 0)
 
         expect:
         artifact.classifier == 'classifier'
     }
 
     def "attributes are equal when module, name, type, extension and extended attributes are equal"() {
-        ResolvedDependency dependency = dep("group", "module1", "1.2")
-        ResolvedDependency dependencySameModule = dep("group", "module1", "1.2")
-        ResolvedDependency dependency2 = dep("group", "module2", "1-beta")
+        def dependency = dep("group", "module1", "1.2")
+        def dependencySameModule = dep("group", "module1", "1.2")
+        def dependency2 = dep("group", "module2", "1-beta")
         Artifact ivyArt = ivyArtifact("name", "type", "ext", [attr: "value"])
         Artifact ivyArtifactWithDifferentName = ivyArtifact("name2", "type", "ext", [attr: "value"])
         Artifact ivyArtifactWithDifferentType = ivyArtifact("name", "type2", "ext", [attr: "value"])
         Artifact ivyArtifactWithDifferentExt = ivyArtifact("name", "type", "ext2", [attr: "value"])
         Artifact ivyArtWithDifferentAttributes = ivyArtifact("name", "type", "ext", [attr: "value2"])
-        def artifact = new DefaultResolvedArtifact(dependency, ivyArt, artifactSource)
-        def equalArtifact = new DefaultResolvedArtifact(dependencySameModule, ivyArt, artifactSource)
-        def differentModule = new DefaultResolvedArtifact(dependency2, ivyArt, artifactSource)
-        def differentName = new DefaultResolvedArtifact(dependency, ivyArtifactWithDifferentName, artifactSource)
-        def differentType = new DefaultResolvedArtifact(dependency, ivyArtifactWithDifferentType, artifactSource)
-        def differentExtension = new DefaultResolvedArtifact(dependency, ivyArtifactWithDifferentExt, artifactSource)
-        def differentAttributes = new DefaultResolvedArtifact(dependency, ivyArtWithDifferentAttributes, artifactSource)
+        def artifact = new DefaultResolvedArtifact(dependency, {} as Factory, ivyArt, artifactSource, 0)
+        def equalArtifact = new DefaultResolvedArtifact(dependencySameModule, {} as Factory, ivyArt, artifactSource, 0)
+        def differentModule = new DefaultResolvedArtifact(dependency2, {} as Factory, ivyArt, artifactSource, 0)
+        def differentName = new DefaultResolvedArtifact(dependency, {} as Factory, ivyArtifactWithDifferentName, artifactSource, 0)
+        def differentType = new DefaultResolvedArtifact(dependency, {} as Factory, ivyArtifactWithDifferentType, artifactSource, 0)
+        def differentExtension = new DefaultResolvedArtifact(dependency, {} as Factory, ivyArtifactWithDifferentExt, artifactSource, 0)
+        def differentAttributes = new DefaultResolvedArtifact(dependency, {} as Factory, ivyArtWithDifferentAttributes, artifactSource, 0)
 
         expect:
         artifact Matchers.strictlyEqual(equalArtifact)
@@ -70,10 +69,8 @@ class DefaultResolvedArtifactTest extends Specification {
     }
 
     def dep(String group, String moduleName, String version) {
-        ResolvedDependency dependency = Mock()
         ResolvedModuleVersion module = Mock()
-        _ * dependency.module >> module
         _ * module.id >> new DefaultModuleVersionIdentifier(group, moduleName, version)
-        return dependency
+        module
     }
 }
diff --git a/subprojects/core-impl/src/test/groovy/org/gradle/api/internal/artifacts/DefaultResolvedDependencySpec.groovy b/subprojects/core-impl/src/test/groovy/org/gradle/api/internal/artifacts/DefaultResolvedDependencySpec.groovy
index 35836cf..a28a53f 100644
--- a/subprojects/core-impl/src/test/groovy/org/gradle/api/internal/artifacts/DefaultResolvedDependencySpec.groovy
+++ b/subprojects/core-impl/src/test/groovy/org/gradle/api/internal/artifacts/DefaultResolvedDependencySpec.groovy
@@ -19,7 +19,7 @@ import org.gradle.api.artifacts.ResolvedArtifact
 import spock.lang.Specification
 
 class DefaultResolvedDependencySpec extends Specification {
-    final DefaultResolvedDependency dependency = new DefaultResolvedDependency("name", "group", "module", "version", "config")
+    final dependency = new DefaultResolvedDependency(DefaultModuleVersionIdentifier.newId("group", "module", "version"), "config")
 
     def "provides meta-data about the module"() {
         expect:
diff --git a/subprojects/core-impl/src/test/groovy/org/gradle/api/internal/artifacts/DefaultResolvedDependencyTest.java b/subprojects/core-impl/src/test/groovy/org/gradle/api/internal/artifacts/DefaultResolvedDependencyTest.java
index 1d110a5..4a011d9 100644
--- a/subprojects/core-impl/src/test/groovy/org/gradle/api/internal/artifacts/DefaultResolvedDependencyTest.java
+++ b/subprojects/core-impl/src/test/groovy/org/gradle/api/internal/artifacts/DefaultResolvedDependencyTest.java
@@ -15,10 +15,15 @@
  */
 package org.gradle.api.internal.artifacts;
 
+import org.apache.ivy.core.module.descriptor.Artifact;
 import org.gradle.api.InvalidUserDataException;
 import org.gradle.api.artifacts.ResolvedArtifact;
 import org.gradle.api.artifacts.ResolvedDependency;
+import org.gradle.api.artifacts.ResolvedModuleVersion;
+import org.gradle.internal.Factory;
 import org.gradle.util.JUnit4GroovyMockery;
+import org.jmock.Expectations;
+import org.jmock.Mockery;
 import org.jmock.integration.junit4.JUnit4Mockery;
 import org.junit.Test;
 
@@ -28,16 +33,13 @@ import java.util.Set;
 
 import static com.google.common.collect.Iterables.concat;
 import static com.google.common.collect.Sets.newHashSet;
-import static org.gradle.api.artifacts.ArtifactsTestUtils.createResolvedArtifact;
+import static org.gradle.api.internal.artifacts.DefaultModuleVersionIdentifier.newId;
 import static org.gradle.util.Matchers.strictlyEqual;
 import static org.gradle.util.WrapUtil.toSet;
 import static org.hamcrest.Matchers.equalTo;
 import static org.hamcrest.Matchers.not;
 import static org.junit.Assert.assertThat;
 
-/**
- * @author Hans Dockter
- */
 public class DefaultResolvedDependencyTest {
     private JUnit4Mockery context = new JUnit4GroovyMockery();
 
@@ -47,7 +49,7 @@ public class DefaultResolvedDependencyTest {
         String someName = "someName";
         String someVersion = "someVersion";
         String someConfiguration = "someConfiguration";
-        DefaultResolvedDependency resolvedDependency = new DefaultResolvedDependency(someGroup, someName, someVersion, someConfiguration);
+        DefaultResolvedDependency resolvedDependency = new DefaultResolvedDependency(newId(someGroup, someName, someVersion), someConfiguration);
         assertThat(resolvedDependency.getName(), equalTo(someGroup + ":" + someName + ":" + someVersion));
         assertThat(resolvedDependency.getModuleGroup(), equalTo(someGroup));
         assertThat(resolvedDependency.getModuleName(), equalTo(someName));
@@ -62,9 +64,9 @@ public class DefaultResolvedDependencyTest {
     public void getAllModuleArtifacts() {
         ResolvedArtifact moduleArtifact = createArtifact("moduleArtifact");
         ResolvedArtifact childModuleArtifact = createArtifact("childModuleArtifact");
-        DefaultResolvedDependency resolvedDependency = new DefaultResolvedDependency("someGroup", "someName", "someVersion", "someConfiguration");
+        DefaultResolvedDependency resolvedDependency = new DefaultResolvedDependency(newId("someGroup", "someName", "someVersion"), "someConfiguration");
         resolvedDependency.addModuleArtifact(moduleArtifact);
-        DefaultResolvedDependency childDependency = new DefaultResolvedDependency("someGroup", "someChild", "someVersion", "someChildConfiguration");
+        DefaultResolvedDependency childDependency = new DefaultResolvedDependency(newId("someGroup", "someChild", "someVersion"), "someChildConfiguration");
         childDependency.addModuleArtifact(childModuleArtifact);
         resolvedDependency.getChildren().add(childDependency);
         assertThat(resolvedDependency.getAllModuleArtifacts(), equalTo(toSet(moduleArtifact, childModuleArtifact)));
@@ -88,8 +90,40 @@ public class DefaultResolvedDependencyTest {
         return createResolvedArtifact(context, name, "someType", "someExt", new File("pathTo" + name));
     }
 
+    public static DefaultResolvedArtifact createResolvedArtifact(final Mockery context, final String name, final String type, final String extension, final File file) {
+        final Artifact artifactStub = context.mock(Artifact.class, "artifact" + name);
+        context.checking(new Expectations() {{
+            allowing(artifactStub).getName();
+            will(returnValue(name));
+            allowing(artifactStub).getType();
+            will(returnValue(type));
+            allowing(artifactStub).getExt();
+            will(returnValue(extension));
+            allowing(artifactStub).getExtraAttributes();
+            will(returnValue(Collections.emptyMap()));
+            allowing(artifactStub).getQualifiedExtraAttributes();
+            will(returnValue(Collections.emptyMap()));
+            allowing(artifactStub).getExtraAttribute(with(org.hamcrest.Matchers.notNullValue(String.class)));
+            will(returnValue(null));
+        }});
+        final Factory artifactSource = context.mock(Factory.class);
+        context.checking(new Expectations() {{
+            allowing(artifactSource).create();
+            will(returnValue(file));
+        }});
+        final ResolvedDependency resolvedDependency = context.mock(ResolvedDependency.class);
+        final ResolvedModuleVersion version = context.mock(ResolvedModuleVersion.class);
+        context.checking(new Expectations() {{
+            allowing(resolvedDependency).getModule();
+            will(returnValue(version));
+            allowing(version).getId();
+            will(returnValue(new DefaultModuleVersionIdentifier("group", name, "1.2")));
+        }});
+        return new DefaultResolvedArtifact(resolvedDependency.getModule(), null , artifactStub, artifactSource, 0);
+    }
+
     private DefaultResolvedDependency createResolvedDependency() {
-        return new DefaultResolvedDependency("someGroup", "someName", "someVersion", "someConfiguration");
+        return new DefaultResolvedDependency(newId("someGroup", "someName", "someVersion"), "someConfiguration");
     }
 
     @Test
@@ -106,7 +140,7 @@ public class DefaultResolvedDependencyTest {
     public void getArtifactsWithParentWithoutParentArtifacts() {
         DefaultResolvedDependency resolvedDependency = createResolvedDependency();
 
-        DefaultResolvedDependency parent = new DefaultResolvedDependency("someGroup", "parent", "someVersion", "someConfiguration");
+        DefaultResolvedDependency parent = new DefaultResolvedDependency(newId("someGroup", "parent", "someVersion"), "someConfiguration");
         resolvedDependency.getParents().add(parent);
         assertThat(resolvedDependency.getArtifacts(parent), equalTo(Collections.<ResolvedArtifact>emptySet()));
     }
@@ -115,7 +149,7 @@ public class DefaultResolvedDependencyTest {
     public void getParentArtifactsWithParentWithoutParentArtifacts() {
         DefaultResolvedDependency resolvedDependency = createResolvedDependency();
 
-        DefaultResolvedDependency parent = new DefaultResolvedDependency("someGroup", "parent", "someVersion", "someConfiguration");
+        DefaultResolvedDependency parent = new DefaultResolvedDependency(newId("someGroup", "parent", "someVersion"), "someConfiguration");
         resolvedDependency.getParents().add(parent);
         assertThat(resolvedDependency.getParentArtifacts(parent), equalTo(Collections.<ResolvedArtifact>emptySet()));
     }
@@ -123,7 +157,7 @@ public class DefaultResolvedDependencyTest {
     @Test(expected = InvalidUserDataException.class)
     public void getParentArtifactsWithUnknownParent() {
         DefaultResolvedDependency resolvedDependency = createResolvedDependency();
-        DefaultResolvedDependency unknownParent = new DefaultResolvedDependency("someGroup", "parent2", "someVersion", "someConfiguration");
+        DefaultResolvedDependency unknownParent = new DefaultResolvedDependency(newId("someGroup", "parent2", "someVersion"), "someConfiguration");
         assertThat(resolvedDependency.getParentArtifacts(unknownParent),
                 equalTo(Collections.<ResolvedArtifact>emptySet()));
     }
@@ -133,7 +167,7 @@ public class DefaultResolvedDependencyTest {
         Set<ResolvedArtifact> someModuleArtifacts = toSet(createArtifact("someModuleResolvedArtifact"));
         DefaultResolvedDependency resolvedDependency = createResolvedDependency();
 
-        DefaultResolvedDependency unknownParent = new DefaultResolvedDependency("someGroup", "parent2", "someVersion", "someConfiguration");
+        DefaultResolvedDependency unknownParent = new DefaultResolvedDependency(newId("someGroup", "parent2", "someVersion"), "someConfiguration");
         assertThat(resolvedDependency.getParentArtifacts(unknownParent),
                 equalTo(someModuleArtifacts));
     }
@@ -147,7 +181,7 @@ public class DefaultResolvedDependencyTest {
 
         createAndAddParent("parent2", resolvedDependency, newHashSet(createArtifact("parent2Specific")));
 
-        DefaultResolvedDependency child = new DefaultResolvedDependency("someGroup", "someChild", "someVersion", "someChildConfiguration");
+        DefaultResolvedDependency child = new DefaultResolvedDependency(newId("someGroup", "someChild", "someVersion"), "someChildConfiguration");
         resolvedDependency.getChildren().add(child);
 
         Set<ResolvedArtifact> childParent1SpecificArtifacts = newHashSet(createArtifact("childParent1Specific"));
@@ -162,12 +196,12 @@ public class DefaultResolvedDependencyTest {
 
     @Test
     public void equalsAndHashCode() {
-        DefaultResolvedDependency dependency = new DefaultResolvedDependency("group", "name", "version", "config");
-        DefaultResolvedDependency same = new DefaultResolvedDependency("group", "name", "version", "config");
-        DefaultResolvedDependency differentGroup = new DefaultResolvedDependency("other", "name", "version", "config");
-        DefaultResolvedDependency differentName = new DefaultResolvedDependency("group", "other", "version", "config");
-        DefaultResolvedDependency differentVersion = new DefaultResolvedDependency("group", "name", "other", "config");
-        DefaultResolvedDependency differentConfiguration = new DefaultResolvedDependency("group", "name", "version", "other");
+        DefaultResolvedDependency dependency = new DefaultResolvedDependency(newId("group", "name", "version"), "config");
+        DefaultResolvedDependency same = new DefaultResolvedDependency(newId("group", "name", "version"), "config");
+        DefaultResolvedDependency differentGroup = new DefaultResolvedDependency(newId("other", "name", "version"), "config");
+        DefaultResolvedDependency differentName = new DefaultResolvedDependency(newId("group", "other", "version"), "config");
+        DefaultResolvedDependency differentVersion = new DefaultResolvedDependency(newId("group", "name", "other"), "config");
+        DefaultResolvedDependency differentConfiguration = new DefaultResolvedDependency(newId("group", "name", "version"), "other");
 
         assertThat(dependency, strictlyEqual(same));
         assertThat(dependency, not(equalTo(differentGroup)));
@@ -177,7 +211,7 @@ public class DefaultResolvedDependencyTest {
     }
 
     private DefaultResolvedDependency createAndAddParent(String parentName, DefaultResolvedDependency resolvedDependency, Set<ResolvedArtifact> parentSpecificArtifacts) {
-        DefaultResolvedDependency parent = new DefaultResolvedDependency("someGroup", parentName, "someVersion", "someConfiguration");
+        DefaultResolvedDependency parent = new DefaultResolvedDependency(newId("someGroup", parentName, "someVersion"), "someConfiguration");
         resolvedDependency.getParents().add(parent);
         resolvedDependency.addParentSpecificArtifacts(parent, parentSpecificArtifacts);
         return parent;
diff --git a/subprojects/core-impl/src/test/groovy/org/gradle/api/internal/artifacts/ModuleVersionSelectorSerializerTest.groovy b/subprojects/core-impl/src/test/groovy/org/gradle/api/internal/artifacts/ModuleVersionSelectorSerializerTest.groovy
new file mode 100644
index 0000000..40e3c28
--- /dev/null
+++ b/subprojects/core-impl/src/test/groovy/org/gradle/api/internal/artifacts/ModuleVersionSelectorSerializerTest.groovy
@@ -0,0 +1,33 @@
+/*
+ * Copyright 2013 the original author or authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY 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.messaging.serialize.SerializerSpec
+
+import static org.gradle.api.internal.artifacts.DefaultModuleVersionSelector.newSelector
+
+class ModuleVersionSelectorSerializerTest extends SerializerSpec {
+    private serializer = new ModuleVersionSelectorSerializer()
+
+    def "serializes"() {
+        when:
+        def result = serialize(newSelector("org", "foo", "5.0"), serializer)
+
+        then:
+        result == newSelector("org", "foo", "5.0")
+    }
+}
diff --git a/subprojects/core-impl/src/test/groovy/org/gradle/api/internal/artifacts/ResolvedConfigurationIdentifierSerializerTest.groovy b/subprojects/core-impl/src/test/groovy/org/gradle/api/internal/artifacts/ResolvedConfigurationIdentifierSerializerTest.groovy
new file mode 100644
index 0000000..233d792
--- /dev/null
+++ b/subprojects/core-impl/src/test/groovy/org/gradle/api/internal/artifacts/ResolvedConfigurationIdentifierSerializerTest.groovy
@@ -0,0 +1,37 @@
+/*
+ * Copyright 2012 the original author or authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY 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.messaging.serialize.SerializerSpec
+
+import static org.gradle.api.internal.artifacts.DefaultModuleVersionIdentifier.newId
+
+class ResolvedConfigurationIdentifierSerializerTest extends SerializerSpec {
+
+    def s = new ResolvedConfigurationIdentifierSerializer()
+
+    def "serializes"() {
+        def id = newId("org", "foo", "2.0")
+
+        when:
+        def out = serialize(new ResolvedConfigurationIdentifier(id, "conf"), s)
+
+        then:
+        out.configuration == "conf"
+        out.id == id
+    }
+}
diff --git a/subprojects/core-impl/src/test/groovy/org/gradle/api/internal/artifacts/ResolvedConfigurationIdentifierSpec.groovy b/subprojects/core-impl/src/test/groovy/org/gradle/api/internal/artifacts/ResolvedConfigurationIdentifierSpec.groovy
index e975224..441ce80 100644
--- a/subprojects/core-impl/src/test/groovy/org/gradle/api/internal/artifacts/ResolvedConfigurationIdentifierSpec.groovy
+++ b/subprojects/core-impl/src/test/groovy/org/gradle/api/internal/artifacts/ResolvedConfigurationIdentifierSpec.groovy
@@ -21,15 +21,17 @@ package org.gradle.api.internal.artifacts
 import spock.lang.Specification
 import org.gradle.util.Matchers
 
+import static org.gradle.api.internal.artifacts.DefaultModuleVersionIdentifier.*
+
 class ResolvedConfigurationIdentifierSpec extends Specification {
     def equalsAndHashCode() {
         when:
-        ResolvedConfigurationIdentifier id = new ResolvedConfigurationIdentifier('group', 'name', 'version', 'config')
-        ResolvedConfigurationIdentifier same = new ResolvedConfigurationIdentifier('group', 'name', 'version', 'config')
-        ResolvedConfigurationIdentifier differentGroup = new ResolvedConfigurationIdentifier('other', 'name', 'version', 'config')
-        ResolvedConfigurationIdentifier differentName = new ResolvedConfigurationIdentifier('group', 'other', 'version', 'config')
-        ResolvedConfigurationIdentifier differentVersion = new ResolvedConfigurationIdentifier('group', 'name', 'other', 'config')
-        ResolvedConfigurationIdentifier differentConfig = new ResolvedConfigurationIdentifier('group', 'name', 'version', 'other')
+        ResolvedConfigurationIdentifier id = new ResolvedConfigurationIdentifier(newId('group', 'name', 'version'), 'config')
+        ResolvedConfigurationIdentifier same = new ResolvedConfigurationIdentifier(newId('group', 'name', 'version'), 'config')
+        ResolvedConfigurationIdentifier differentGroup = new ResolvedConfigurationIdentifier(newId('other', 'name', 'version'), 'config')
+        ResolvedConfigurationIdentifier differentName = new ResolvedConfigurationIdentifier(newId('group', 'other', 'version'), 'config')
+        ResolvedConfigurationIdentifier differentVersion = new ResolvedConfigurationIdentifier(newId('group', 'name', 'other'), 'config')
+        ResolvedConfigurationIdentifier differentConfig = new ResolvedConfigurationIdentifier(newId('group', 'name', 'version'), 'other')
 
         then:
         Matchers.strictlyEquals(id, same)
diff --git a/subprojects/core-impl/src/test/groovy/org/gradle/api/internal/artifacts/ResolverResultsSpec.groovy b/subprojects/core-impl/src/test/groovy/org/gradle/api/internal/artifacts/ResolverResultsSpec.groovy
index 00529ba..29355f6 100644
--- a/subprojects/core-impl/src/test/groovy/org/gradle/api/internal/artifacts/ResolverResultsSpec.groovy
+++ b/subprojects/core-impl/src/test/groovy/org/gradle/api/internal/artifacts/ResolverResultsSpec.groovy
@@ -21,9 +21,6 @@ import org.gradle.api.artifacts.ResolvedConfiguration
 import org.gradle.api.artifacts.result.ResolutionResult
 import spock.lang.Specification
 
-/**
- * by Szczepan Faber, created at: 10/16/12
- */
 class ResolverResultsSpec extends Specification {
 
     private resolvedConfiguration = Mock(ResolvedConfiguration)
diff --git a/subprojects/core-impl/src/test/groovy/org/gradle/api/internal/artifacts/configurations/ConfigurationsTest.java b/subprojects/core-impl/src/test/groovy/org/gradle/api/internal/artifacts/configurations/ConfigurationsTest.java
index 5d97ed4..3a3ecb1 100644
--- a/subprojects/core-impl/src/test/groovy/org/gradle/api/internal/artifacts/configurations/ConfigurationsTest.java
+++ b/subprojects/core-impl/src/test/groovy/org/gradle/api/internal/artifacts/configurations/ConfigurationsTest.java
@@ -20,18 +20,10 @@ import org.junit.Test;
 
 import static org.junit.Assert.assertThat;
 
-/**
- * @author Hans Dockter
- */
 public class ConfigurationsTest {
     private static final String TEST_CONF = "testConf";
 
     @Test
-    public void testUploadInternalTaskName() {
-        assertThat(Configurations.uploadInternalTaskName(TEST_CONF), Matchers.equalTo("uploadTestConfInternal"));
-    }
-
-    @Test
     public void testUploadTaskName() {
         assertThat(Configurations.uploadTaskName(TEST_CONF), Matchers.equalTo("uploadTestConf"));
     }
diff --git a/subprojects/core-impl/src/test/groovy/org/gradle/api/internal/artifacts/configurations/DefaultConfigurationContainerSpec.groovy b/subprojects/core-impl/src/test/groovy/org/gradle/api/internal/artifacts/configurations/DefaultConfigurationContainerSpec.groovy
index 7c63a99..848d31c 100644
--- a/subprojects/core-impl/src/test/groovy/org/gradle/api/internal/artifacts/configurations/DefaultConfigurationContainerSpec.groovy
+++ b/subprojects/core-impl/src/test/groovy/org/gradle/api/internal/artifacts/configurations/DefaultConfigurationContainerSpec.groovy
@@ -46,7 +46,7 @@ public class DefaultConfigurationContainerSpec extends Specification {
                 resolver, listenerManager, metaDataProvider, _ as ResolutionStrategyInternal) >> conf
 
         when:
-        def compile = configurationContainer.add("compile")
+        def compile = configurationContainer.create("compile")
 
         then:
         configurationContainer.getByName("compile") == compile
diff --git a/subprojects/core-impl/src/test/groovy/org/gradle/api/internal/artifacts/configurations/DefaultConfigurationContainerTest.groovy b/subprojects/core-impl/src/test/groovy/org/gradle/api/internal/artifacts/configurations/DefaultConfigurationContainerTest.groovy
index 1cd233a..6efd9ed 100644
--- a/subprojects/core-impl/src/test/groovy/org/gradle/api/internal/artifacts/configurations/DefaultConfigurationContainerTest.groovy
+++ b/subprojects/core-impl/src/test/groovy/org/gradle/api/internal/artifacts/configurations/DefaultConfigurationContainerTest.groovy
@@ -35,9 +35,6 @@ import org.junit.runner.RunWith
 import static org.hamcrest.Matchers.*
 import static org.junit.Assert.assertThat
 
-/**
- * @author Hans Dockter
- */
 
 @RunWith(JMock)
 class DefaultConfigurationContainerTest {
@@ -47,7 +44,7 @@ class DefaultConfigurationContainerTest {
     private ListenerManager listenerManager = context.mock(ListenerManager.class)
     private DependencyMetaDataProvider metaDataProvider = context.mock(DependencyMetaDataProvider.class)
     private Instantiator instantiator = new ClassGeneratorBackedInstantiator(new AsmBackedClassGenerator(), new DirectInstantiator())
-    private DefaultConfigurationContainer configurationHandler = instantiator.newInstance(DefaultConfigurationContainer.class,
+    private DefaultConfigurationContainer configurationContainer = instantiator.newInstance(DefaultConfigurationContainer.class,
             resolver, instantiator, { name -> name } as DomainObjectContext,
             listenerManager, metaDataProvider)
 
@@ -60,42 +57,42 @@ class DefaultConfigurationContainerTest {
 
     @Test
     void addsNewConfigurationWhenConfiguringSelf() {
-        configurationHandler.configure {
+        configurationContainer.configure {
             newConf
         }
-        assertThat(configurationHandler.findByName('newConf'), notNullValue())
-        assertThat(configurationHandler.newConf, notNullValue())
+        assertThat(configurationContainer.findByName('newConf'), notNullValue())
+        assertThat(configurationContainer.newConf, notNullValue())
     }
 
     @Test(expected = UnknownConfigurationException)
     void doesNotAddNewConfigurationWhenNotConfiguringSelf() {
-        configurationHandler.getByName('unknown')
+        configurationContainer.getByName('unknown')
     }
 
     @Test
     void makesExistingConfigurationAvailableAsProperty() {
-        Configuration configuration = configurationHandler.add('newConf')
+        Configuration configuration = configurationContainer.create('newConf')
         assertThat(configuration, notNullValue())
-        assertThat(configurationHandler.getByName("newConf"), sameInstance(configuration))
-        assertThat(configurationHandler.newConf, sameInstance(configuration))
+        assertThat(configurationContainer.getByName("newConf"), sameInstance(configuration))
+        assertThat(configurationContainer.newConf, sameInstance(configuration))
     }
 
     @Test
     void addsNewConfigurationWithClosureWhenConfiguringSelf() {
         String someDesc = 'desc1'
-        configurationHandler.configure {
+        configurationContainer.configure {
             newConf {
                 description = someDesc
             }
         }
-        assertThat(configurationHandler.newConf.getDescription(), equalTo(someDesc))
+        assertThat(configurationContainer.newConf.getDescription(), equalTo(someDesc))
     }
 
     @Test
     void makesExistingConfigurationAvailableAsConfigureMethod() {
         String someDesc = 'desc1'
-        configurationHandler.add('newConf')
-        Configuration configuration = configurationHandler.newConf {
+        configurationContainer.create('newConf')
+        Configuration configuration = configurationContainer.newConf {
             description = someDesc
         }
         assertThat(configuration.getDescription(), equalTo(someDesc))
@@ -104,8 +101,8 @@ class DefaultConfigurationContainerTest {
     @Test
     void makesExistingConfigurationAvailableAsConfigureMethodWhenConfiguringSelf() {
         String someDesc = 'desc1'
-        Configuration configuration = configurationHandler.add('newConf')
-        configurationHandler.configure {
+        Configuration configuration = configurationContainer.create('newConf')
+        configurationContainer.configure {
             newConf {
                 description = someDesc
             }
@@ -115,6 +112,6 @@ class DefaultConfigurationContainerTest {
 
     @Test(expected = MissingMethodException)
     void newConfigurationWithNonClosureParametersShouldThrowMissingMethodEx() {
-        configurationHandler.newConf('a', 'b')
+        configurationContainer.newConf('a', 'b')
     }
 }
diff --git a/subprojects/core-impl/src/test/groovy/org/gradle/api/internal/artifacts/configurations/DefaultConfigurationTest.java b/subprojects/core-impl/src/test/groovy/org/gradle/api/internal/artifacts/configurations/DefaultConfigurationTest.java
index 5384568..736253f 100644
--- a/subprojects/core-impl/src/test/groovy/org/gradle/api/internal/artifacts/configurations/DefaultConfigurationTest.java
+++ b/subprojects/core-impl/src/test/groovy/org/gradle/api/internal/artifacts/configurations/DefaultConfigurationTest.java
@@ -270,7 +270,7 @@ public class DefaultConfigurationTest {
 
     @Test
     public void filesWithClosureSpec() {
-        Closure closure = HelperUtil.toClosure("{ dep -> dep.group == 'group1' }");
+        Closure closure = TestUtil.toClosure("{ dep -> dep.group == 'group1' }");
         final Set<File> fileSet = toSet(new File("somePath"));
         prepareForFilesBySpec(fileSet);
         assertThat(configuration.files(closure), equalTo(fileSet));
@@ -279,7 +279,7 @@ public class DefaultConfigurationTest {
 
     @Test
     public void fileCollectionWithClosureSpec() {
-        Closure closure = HelperUtil.toClosure("{ dep -> dep.group == 'group1' }");
+        Closure closure = TestUtil.toClosure("{ dep -> dep.group == 'group1' }");
         DefaultConfiguration.ConfigurationFileCollection fileCollection = (DefaultConfiguration.ConfigurationFileCollection)
                 configuration.fileCollection(closure);
         assertThat(fileCollection.getDependencySpec().isSatisfiedBy(createDependency("group1", "name", "version")),
@@ -680,7 +680,7 @@ public class DefaultConfigurationTest {
             one(closure).call(artifact);
         }});
 
-        configuration.getArtifacts().whenObjectAdded(HelperUtil.toClosure(closure));
+        configuration.getArtifacts().whenObjectAdded(TestUtil.toClosure(closure));
         configuration.getArtifacts().add(artifact);
     }
 
@@ -695,7 +695,7 @@ public class DefaultConfigurationTest {
             one(closure).call(artifact);
         }});
 
-        configuration.getArtifacts().whenObjectRemoved(HelperUtil.toClosure(closure));
+        configuration.getArtifacts().whenObjectRemoved(TestUtil.toClosure(closure));
 
         configuration.getArtifacts().remove(artifact);
     }
@@ -755,7 +755,7 @@ public class DefaultConfigurationTest {
         Set<Dependency> expectedDependenciesToCopy = new HashSet<Dependency>(configuration.getDependencies());
         configuration.getDependencies().add(createDependency("group3", "name3", "version3"));
 
-        Closure specClosure = HelperUtil.toClosure("{ element ->  !element.group.equals(\"group3\")}");
+        Closure specClosure = TestUtil.toClosure("{ element ->  !element.group.equals(\"group3\")}");
         Configuration copiedConfiguration = configuration.copy(specClosure);
 
         assertThatCopiedConfigurationHasElementsAndName(copiedConfiguration, expectedDependenciesToCopy);
@@ -800,7 +800,7 @@ public class DefaultConfigurationTest {
         Set<Dependency> expectedDependenciesToCopy = new HashSet<Dependency>(configuration.getAllDependencies());
         configuration.getDependencies().add(createDependency("group3", "name3", "version3"));
 
-        Closure specClosure = HelperUtil.toClosure("{ element ->  !element.group.equals(\"group3\")}");
+        Closure specClosure = TestUtil.toClosure("{ element ->  !element.group.equals(\"group3\")}");
         Configuration copiedConfiguration = configuration.copyRecursive(specClosure);
 
         assertThatCopiedConfigurationHasElementsAndName(copiedConfiguration, expectedDependenciesToCopy);
diff --git a/subprojects/core-impl/src/test/groovy/org/gradle/api/internal/artifacts/dsl/ArtifactFileTest.groovy b/subprojects/core-impl/src/test/groovy/org/gradle/api/internal/artifacts/dsl/ArtifactFileTest.groovy
index 878e386..6326326 100644
--- a/subprojects/core-impl/src/test/groovy/org/gradle/api/internal/artifacts/dsl/ArtifactFileTest.groovy
+++ b/subprojects/core-impl/src/test/groovy/org/gradle/api/internal/artifacts/dsl/ArtifactFileTest.groovy
@@ -16,9 +16,6 @@
 package org.gradle.api.internal.artifacts.dsl
 
 import spock.lang.Specification
-/**
- * @author Hans Dockter
- */
 public class ArtifactFileTest extends Specification {
     final String module = '1.2'
 
diff --git a/subprojects/core-impl/src/test/groovy/org/gradle/api/internal/artifacts/dsl/DefaultArtifactHandlerTest.groovy b/subprojects/core-impl/src/test/groovy/org/gradle/api/internal/artifacts/dsl/DefaultArtifactHandlerTest.groovy
index a31f49b..52b7ef5 100644
--- a/subprojects/core-impl/src/test/groovy/org/gradle/api/internal/artifacts/dsl/DefaultArtifactHandlerTest.groovy
+++ b/subprojects/core-impl/src/test/groovy/org/gradle/api/internal/artifacts/dsl/DefaultArtifactHandlerTest.groovy
@@ -16,18 +16,15 @@
 
 package org.gradle.api.internal.artifacts.dsl
 
-import org.gradle.api.artifacts.PublishArtifactSet
 import org.gradle.api.artifacts.Configuration
 import org.gradle.api.artifacts.ConfigurationContainer
 import org.gradle.api.artifacts.PublishArtifact
+import org.gradle.api.artifacts.PublishArtifactSet
 import org.gradle.api.internal.artifacts.publish.DefaultPublishArtifact
+import org.gradle.api.internal.notations.api.NotationParser
 import org.gradle.util.JUnit4GroovyMockery
 import spock.lang.Specification
-import org.gradle.api.internal.notations.api.NotationParser
 
-/**
- * @author Hans Dockter
- */
 class DefaultArtifactHandlerTest extends Specification {
 
     private static final String TEST_CONF_NAME = "someConf"
diff --git a/subprojects/core-impl/src/test/groovy/org/gradle/api/internal/artifacts/dsl/ModuleVersionSelectorParsersTest.groovy b/subprojects/core-impl/src/test/groovy/org/gradle/api/internal/artifacts/dsl/ModuleVersionSelectorParsersTest.groovy
index f42522c..03dbf4b 100644
--- a/subprojects/core-impl/src/test/groovy/org/gradle/api/internal/artifacts/dsl/ModuleVersionSelectorParsersTest.groovy
+++ b/subprojects/core-impl/src/test/groovy/org/gradle/api/internal/artifacts/dsl/ModuleVersionSelectorParsersTest.groovy
@@ -24,9 +24,6 @@ import static org.gradle.api.internal.artifacts.DefaultModuleVersionSelector.new
 import static org.gradle.api.internal.artifacts.dsl.ModuleVersionSelectorParsers.multiParser
 import static org.gradle.api.internal.artifacts.dsl.ModuleVersionSelectorParsers.parser
 
-/**
- * by Szczepan Faber, created at: 10/14/11
- */
 public class ModuleVersionSelectorParsersTest extends Specification {
 
     def "understands group:name:version notation"() {
diff --git a/subprojects/core-impl/src/test/groovy/org/gradle/api/internal/artifacts/dsl/PublishArtifactNotationParserFactoryTest.groovy b/subprojects/core-impl/src/test/groovy/org/gradle/api/internal/artifacts/dsl/PublishArtifactNotationParserFactoryTest.groovy
index 4d2bf8f..1ca5ee6 100644
--- a/subprojects/core-impl/src/test/groovy/org/gradle/api/internal/artifacts/dsl/PublishArtifactNotationParserFactoryTest.groovy
+++ b/subprojects/core-impl/src/test/groovy/org/gradle/api/internal/artifacts/dsl/PublishArtifactNotationParserFactoryTest.groovy
@@ -30,9 +30,6 @@ import spock.lang.Specification
 
 import java.awt.*
 
-/**
- * @author Hans Dockter
- */
 public class PublishArtifactNotationParserFactoryTest extends Specification {
     final DependencyMetaDataProvider provider = Mock()
     final Instantiator instantiator = ThreadGlobalInstantiator.getOrCreate()
diff --git a/subprojects/core-impl/src/test/groovy/org/gradle/api/internal/artifacts/ivyservice/DefaultBuildableArtifactResolveResultTest.groovy b/subprojects/core-impl/src/test/groovy/org/gradle/api/internal/artifacts/ivyservice/DefaultBuildableArtifactResolveResultTest.groovy
index 292b4bc..2eacaa9 100644
--- a/subprojects/core-impl/src/test/groovy/org/gradle/api/internal/artifacts/ivyservice/DefaultBuildableArtifactResolveResultTest.groovy
+++ b/subprojects/core-impl/src/test/groovy/org/gradle/api/internal/artifacts/ivyservice/DefaultBuildableArtifactResolveResultTest.groovy
@@ -16,8 +16,7 @@
 
 package org.gradle.api.internal.artifacts.ivyservice
 
-import org.apache.ivy.core.module.descriptor.DefaultArtifact
-import org.apache.ivy.core.module.id.ModuleRevisionId
+import org.gradle.api.artifacts.ArtifactIdentifier
 import org.gradle.api.internal.artifacts.ivyservice.ivyresolve.ArtifactNotFoundException
 import org.gradle.api.internal.artifacts.ivyservice.ivyresolve.ArtifactResolveException
 import spock.lang.Specification
@@ -57,7 +56,7 @@ class DefaultBuildableArtifactResolveResultTest extends Specification {
 
     def "fails with not found exception when artifact not found"() {
         when:
-        result.notFound(new DefaultArtifact(ModuleRevisionId.newInstance("org", "module", "rev"), new Date(), "art", "type", "type"))
+        result.notFound(Stub(ArtifactIdentifier))
 
         then:
         result.failure instanceof ArtifactNotFoundException
diff --git a/subprojects/core-impl/src/test/groovy/org/gradle/api/internal/artifacts/ivyservice/DefaultDependencyResolveDetailsSpec.groovy b/subprojects/core-impl/src/test/groovy/org/gradle/api/internal/artifacts/ivyservice/DefaultDependencyResolveDetailsSpec.groovy
index 3cb3df0..16e513a 100644
--- a/subprojects/core-impl/src/test/groovy/org/gradle/api/internal/artifacts/ivyservice/DefaultDependencyResolveDetailsSpec.groovy
+++ b/subprojects/core-impl/src/test/groovy/org/gradle/api/internal/artifacts/ivyservice/DefaultDependencyResolveDetailsSpec.groovy
@@ -16,14 +16,11 @@
 
 package org.gradle.api.internal.artifacts.ivyservice
 
+import org.gradle.api.internal.artifacts.ivyservice.resolveengine.result.VersionSelectionReasons
 import spock.lang.Specification
 
 import static org.gradle.api.internal.artifacts.DefaultModuleVersionSelector.newSelector
-import org.gradle.api.internal.artifacts.ivyservice.resolveengine.result.VersionSelectionReasons
 
-/**
- * by Szczepan Faber, created at: 12/13/12
- */
 class DefaultDependencyResolveDetailsSpec extends Specification {
 
     def "can specify version to use"() {
diff --git a/subprojects/core-impl/src/test/groovy/org/gradle/api/internal/artifacts/ivyservice/DefaultIvyContextManagerTest.groovy b/subprojects/core-impl/src/test/groovy/org/gradle/api/internal/artifacts/ivyservice/DefaultIvyContextManagerTest.groovy
new file mode 100644
index 0000000..099072c
--- /dev/null
+++ b/subprojects/core-impl/src/test/groovy/org/gradle/api/internal/artifacts/ivyservice/DefaultIvyContextManagerTest.groovy
@@ -0,0 +1,225 @@
+/*
+ * Copyright 2013 the original author or authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY 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.Ivy
+import org.apache.ivy.core.IvyContext
+import org.apache.ivy.plugins.resolver.DependencyResolver
+import org.gradle.api.Action
+import org.gradle.api.Transformer
+import org.gradle.test.fixtures.concurrent.ConcurrentSpec
+
+class DefaultIvyContextManagerTest extends ConcurrentSpec {
+    final manager = new DefaultIvyContextManager()
+
+    def setup() {
+        IvyContext.currentStack.clear()
+    }
+
+    def "executes action against an Ivy instance"() {
+        def action = Mock(Action)
+
+        when:
+        manager.withIvy(action)
+
+        then:
+        1 * action.execute({it != null})
+        0 * action._
+    }
+
+    def "executes action against an Ivy instance and returns the result"() {
+        def action = Mock(Transformer)
+
+        when:
+        def result = manager.withIvy(action)
+
+        then:
+        result == "result"
+
+        and:
+        1 * action.transform({it != null}) >> "result"
+        0 * action._
+    }
+
+    def "nested actions are executed against the same Ivy instance"() {
+        def action1 = Mock(Action)
+        def action2 = Mock(Action)
+        def transformer = Mock(Transformer)
+        def ivy
+
+        when:
+        manager.withIvy(action1)
+
+        then:
+        1 * action1.execute(_) >> { Ivy param ->
+            ivy = param
+            manager.withIvy(transformer)
+        }
+        1 * transformer.transform(_) >> { Ivy param ->
+            assert param.is(ivy)
+            manager.withIvy(action2)
+        }
+        1 * action2.execute(_) >> { Ivy param ->
+            assert param.is(ivy)
+        }
+        0 * _._
+    }
+
+    def "sets up Ivy context stack and cleans up after action"() {
+        given:
+        def action = Mock(Action)
+
+        when:
+        manager.withIvy(action)
+
+        then:
+        1 * action.execute(_) >> { Ivy ivy ->
+            assert IvyContext.context.ivy.is(ivy)
+        }
+
+        and:
+        IvyContext.currentStack.empty()
+    }
+
+    def "cleans up after failed action"() {
+        given:
+        def action = Mock(Action)
+        def failure = new RuntimeException()
+
+        and:
+        action.execute(_) >> { Ivy ivy ->
+            throw failure
+        }
+
+        when:
+        manager.withIvy(action)
+
+        then:
+        RuntimeException e = thrown()
+        e == failure
+
+        and:
+        IvyContext.currentStack.empty()
+    }
+
+    def "reuses Ivy and IvySettings instances"() {
+        given:
+        def action = Mock(Action)
+        def ivy
+        def ivySettings
+
+        when:
+        manager.withIvy(action)
+
+        then:
+        1 * action.execute(_) >> { Ivy param ->
+            ivy = param
+            ivySettings = param.settings
+        }
+
+        when:
+        manager.withIvy(action)
+
+        then:
+        1 * action.execute(_) >> { Ivy param ->
+            assert param.is(ivy)
+            assert param.settings.is(ivySettings)
+            assert IvyContext.context.ivy.is(ivy)
+        }
+
+        when:
+        async {
+            start {
+                manager.withIvy(action)
+            }
+        }
+
+        then:
+        1 * action.execute(_) >> { Ivy param ->
+            assert param.is(ivy)
+            assert param.settings.is(ivySettings)
+            assert IvyContext.context.ivy.is(ivy)
+        }
+
+        and:
+        IvyContext.currentStack.empty()
+    }
+
+    def "resets Ivy settings on reuse"() {
+        given:
+        def action1 = Mock(Action)
+        def action2 = Mock(Action)
+        def ivy
+        def ivySettings
+
+        when:
+        manager.withIvy(action1)
+
+        then:
+        1 * action1.execute(_) >> { Ivy param ->
+            ivy = param
+            ivySettings = param.settings
+            ivySettings.addResolver(Stub(DependencyResolver) { getName() >> "some-resolver" })
+            ivySettings.setDefaultResolver("some-resolver")
+        }
+
+        when:
+        manager.withIvy(action2)
+
+        then:
+        1 * action2.execute(_) >> { Ivy param ->
+            assert param.settings.is(ivySettings)
+            assert ivySettings.defaultResolverName == null
+            assert ivySettings.resolvers.empty
+            assert ivySettings.resolverNames.empty
+        }
+    }
+
+    def "each thread is given a separate Ivy instance and context"() {
+        given:
+        def ivy1
+        def ivySettings1
+        def ivy2
+        def ivySettings2
+
+        when:
+        async {
+            start {
+                manager.withIvy({ param ->
+                    instant.action1
+                    thread.blockUntil.action2
+                    ivy1 = param
+                    ivySettings1 = param.settings
+                    assert IvyContext.context.ivy.is(ivy1)
+                } as Action)
+            }
+            start {
+                manager.withIvy({ param ->
+                    instant.action2
+                    thread.blockUntil.action1
+                    ivy2 = param
+                    ivySettings2 = param.settings
+                    assert IvyContext.context.ivy.is(ivy2)
+                } as Action)
+            }
+        }
+
+        then:
+        !ivy1.is(ivy2)
+        !ivySettings1.is(ivySettings2)
+    }
+}
diff --git a/subprojects/core-impl/src/test/groovy/org/gradle/api/internal/artifacts/ivyservice/DefaultIvyFactoryTest.groovy b/subprojects/core-impl/src/test/groovy/org/gradle/api/internal/artifacts/ivyservice/DefaultIvyFactoryTest.groovy
deleted file mode 100644
index 521ee61..0000000
--- a/subprojects/core-impl/src/test/groovy/org/gradle/api/internal/artifacts/ivyservice/DefaultIvyFactoryTest.groovy
+++ /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.api.internal.artifacts.ivyservice
-
-import spock.lang.Specification
-import org.apache.ivy.core.settings.IvySettings
-
-class DefaultIvyFactoryTest extends Specification {
-    final DefaultIvyFactory factory = new DefaultIvyFactory()
-
-    def "creates Ivy instance for IvySettings"() {
-        def ivySettings = new IvySettings()
-
-        expect:
-        def ivy = factory.createIvy(ivySettings)
-        ivy.settings == ivySettings
-    }
-
-    def "caches Ivy instance for given IvySettings"() {
-        def ivySettings = new IvySettings()
-
-        expect:
-        def ivy1 = factory.createIvy(ivySettings)
-        def ivy2 = factory.createIvy(ivySettings)
-        ivy1.is(ivy2)
-    }
-}
diff --git a/subprojects/core-impl/src/test/groovy/org/gradle/api/internal/artifacts/ivyservice/DefaultSettingsConverterTest.groovy b/subprojects/core-impl/src/test/groovy/org/gradle/api/internal/artifacts/ivyservice/DefaultSettingsConverterTest.groovy
deleted file mode 100644
index aed2959..0000000
--- a/subprojects/core-impl/src/test/groovy/org/gradle/api/internal/artifacts/ivyservice/DefaultSettingsConverterTest.groovy
+++ /dev/null
@@ -1,114 +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.settings.IvySettings
-import org.apache.ivy.plugins.resolver.DependencyResolver
-import org.apache.ivy.plugins.resolver.IBiblioResolver
-import org.gradle.internal.Factory
-import spock.lang.Specification
-
-class DefaultSettingsConverterTest extends Specification {
-    final DependencyResolver defaultResolver = Mock()
-    final IBiblioResolver testResolver = new IBiblioResolver()
-    final IBiblioResolver testResolver2 = new IBiblioResolver()
-
-    final Factory<IvySettings> ivySettingsFactory = Mock()
-    final IvySettings ivySettings = new IvySettings()
-
-    DefaultSettingsConverter converter = new DefaultSettingsConverter(ivySettingsFactory)
-
-    public void setup() {
-        testResolver.name = 'resolver'
-    }
-
-    public void testConvertForResolve() {
-        when:
-        IvySettings settings = converter.convertForResolve(defaultResolver)
-
-        then:
-        1 * ivySettingsFactory.create() >> ivySettings
-        1 * defaultResolver.setSettings(ivySettings)
-        _ * defaultResolver.getName() >> 'default'
-        0 * _._
-
-        assert settings.is(ivySettings)
-
-        assert settings.defaultResolver == defaultResolver
-        assert settings.resolvers.size() == 1
-    }
-
-    public void shouldReuseResolveSettings() {
-        given:
-        1 * ivySettingsFactory.create() >> ivySettings
-        _ * defaultResolver.getName() >> 'default'
-        IvySettings settings = converter.convertForResolve(defaultResolver)
-        settings.addResolver(testResolver)
-        settings.addResolver(testResolver2)
-
-        when:
-        settings = converter.convertForResolve(defaultResolver)
-
-        then:
-        assert settings.is(ivySettings)
-
-        assert settings.defaultResolver == defaultResolver
-        assert settings.resolvers.size() == 1
-    }
-
-    public void testConvertForPublish() {
-        when:
-        IvySettings settings = converter.convertForPublish([testResolver, testResolver2])
-
-        then:
-        settings.is(ivySettings)
-
-        and:
-        [testResolver, testResolver2].each {
-            it.settings == settings
-            it.repositoryCacheManager.settings == settings
-        }
-
-        and:
-        1 * ivySettingsFactory.create() >> ivySettings
-        0 * _._
-    }
-
-    public void reusesPublishSettings() {
-        when:
-        IvySettings settings = converter.convertForPublish([testResolver])
-
-        then:
-        settings.is(ivySettings)
-
-        and:
-        1 * ivySettingsFactory.create() >> ivySettings
-        0 * _._
-
-        when:
-        settings = converter.convertForPublish([testResolver, testResolver2])
-
-        then:
-        settings.is(ivySettings)
-
-        and:
-            [testResolver, testResolver2].each {
-            it.settings == settings
-            it.repositoryCacheManager.settings == settings
-        }
-    }
-}
diff --git a/subprojects/core-impl/src/test/groovy/org/gradle/api/internal/artifacts/ivyservice/DefaultUnresolvedDependencySpec.groovy b/subprojects/core-impl/src/test/groovy/org/gradle/api/internal/artifacts/ivyservice/DefaultUnresolvedDependencySpec.groovy
index 585e5c3..93ee3b5 100644
--- a/subprojects/core-impl/src/test/groovy/org/gradle/api/internal/artifacts/ivyservice/DefaultUnresolvedDependencySpec.groovy
+++ b/subprojects/core-impl/src/test/groovy/org/gradle/api/internal/artifacts/ivyservice/DefaultUnresolvedDependencySpec.groovy
@@ -19,9 +19,6 @@ package org.gradle.api.internal.artifacts.ivyservice
 import org.gradle.api.internal.artifacts.DefaultModuleVersionSelector
 import spock.lang.Specification
 
-/**
- * by Szczepan Faber, created at: 5/11/12
- */
 class DefaultUnresolvedDependencySpec extends Specification {
 
     def "provides module details"() {
diff --git a/subprojects/core-impl/src/test/groovy/org/gradle/api/internal/artifacts/ivyservice/ErrorHandlingArtifactDependencyResolverTest.groovy b/subprojects/core-impl/src/test/groovy/org/gradle/api/internal/artifacts/ivyservice/ErrorHandlingArtifactDependencyResolverTest.groovy
index d587aa7..f34650b 100644
--- a/subprojects/core-impl/src/test/groovy/org/gradle/api/internal/artifacts/ivyservice/ErrorHandlingArtifactDependencyResolverTest.groovy
+++ b/subprojects/core-impl/src/test/groovy/org/gradle/api/internal/artifacts/ivyservice/ErrorHandlingArtifactDependencyResolverTest.groovy
@@ -13,9 +13,9 @@
  * See the License for the specific language governing permissions and
  * limitations under the License.
  */
-package org.gradle.api.internal.artifacts.ivyservice;
-
+package org.gradle.api.internal.artifacts.ivyservice
 
+import org.gradle.api.artifacts.LenientConfiguration;
 import org.gradle.api.artifacts.ResolveException
 import org.gradle.api.artifacts.ResolvedConfiguration
 import org.gradle.api.artifacts.result.ResolutionResult
@@ -46,12 +46,13 @@ public class ErrorHandlingArtifactDependencyResolverTest extends Specification {
         outerResults.resolvedConfiguration.hasError()
         outerResults.resolvedConfiguration.rethrowFailure()
         outerResults.resolvedConfiguration.getFiles(Specs.satisfyAll())
+        outerResults.resolutionResult.getRoot()
 
         then:
         1 * resolvedConfiguration.hasError()
         1 * resolvedConfiguration.rethrowFailure()
         1 * resolvedConfiguration.getFiles(Specs.satisfyAll())
-        outerResults.resolutionResult == resolutionResult
+        1 * resolutionResult.getRoot()
     }
 
     void "wraps operations with the failure"() {
@@ -79,19 +80,63 @@ public class ErrorHandlingArtifactDependencyResolverTest extends Specification {
         resolvedConfiguration.rethrowFailure() >> { throw failure }
         resolvedConfiguration.getFiles(Specs.satisfyAll()) >> { throw failure }
         resolvedConfiguration.getFirstLevelModuleDependencies() >> { throw failure }
+        resolvedConfiguration.getFirstLevelModuleDependencies(_) >> { throw failure }
         resolvedConfiguration.getResolvedArtifacts() >> { throw failure }
+        resolvedConfiguration.getLenientConfiguration() >> { throw failure }
 
         delegate.resolve(configuration, repositories) >> { new ResolverResults(resolvedConfiguration, resolutionResult) }
 
         when:
-        ResolverResults results = resolver.resolve(configuration, repositories);
+        def result = resolver.resolve(configuration, repositories).resolvedConfiguration
+
+        then:
+        failsWith(failure)
+                .when { result.rethrowFailure() }
+                .when { result.getFiles(Specs.satisfyAll()) }
+                .when { result.firstLevelModuleDependencies }
+                .when { result.getFirstLevelModuleDependencies(Specs.satisfyAll()) }
+                .when { result.resolvedArtifacts }
+                .when { result.lenientConfiguration }
+    }
+
+    void "wraps exceptions thrown by resolved lenient configuration"() {
+        given:
+        def failure = new RuntimeException()
+        def lenientConfiguration = Stub(LenientConfiguration)
+
+        resolvedConfiguration.getLenientConfiguration() >> lenientConfiguration
+        lenientConfiguration.getFiles(_) >> { throw failure }
+        lenientConfiguration.getFirstLevelModuleDependencies(_) >> { throw failure }
+        lenientConfiguration.getArtifacts(_) >> { throw failure }
+        lenientConfiguration.getUnresolvedModuleDependencies() >> { throw failure }
+
+        delegate.resolve(configuration, repositories) >> { new ResolverResults(resolvedConfiguration, resolutionResult) }
+
+        when:
+        def result = resolver.resolve(configuration, repositories).resolvedConfiguration.lenientConfiguration
+
+        then:
+        failsWith(failure)
+                .when { result.getFiles(Specs.satisfyAll()) }
+                .when { result.getFirstLevelModuleDependencies(Specs.satisfyAll()) }
+                .when { result.getArtifacts(Specs.satisfyAll()) }
+                .when { result.unresolvedModuleDependencies }
+    }
+
+    void "wraps exceptions thrown by resolution result"() {
+        given:
+        def failure = new RuntimeException()
+
+        resolutionResult.root >> { throw failure }
+
+        delegate.resolve(configuration, repositories) >> { new ResolverResults(resolvedConfiguration, resolutionResult) }
+
+        when:
+        def result = resolver.resolve(configuration, repositories).resolutionResult
 
         then:
         failsWith(failure)
-                .when { results.resolvedConfiguration.rethrowFailure(); }
-                .when { results.resolvedConfiguration.getFiles(Specs.satisfyAll()); }
-                .when { results.resolvedConfiguration.getFirstLevelModuleDependencies(); }
-                .when { results.resolvedConfiguration.getResolvedArtifacts(); }
+                .when { result.root }
     }
 
     ExceptionFixture failsWith(Throwable failure) {
diff --git a/subprojects/core-impl/src/test/groovy/org/gradle/api/internal/artifacts/ivyservice/IvyBackedArtifactPublisherTest.java b/subprojects/core-impl/src/test/groovy/org/gradle/api/internal/artifacts/ivyservice/IvyBackedArtifactPublisherTest.java
deleted file mode 100644
index 272474e..0000000
--- a/subprojects/core-impl/src/test/groovy/org/gradle/api/internal/artifacts/ivyservice/IvyBackedArtifactPublisherTest.java
+++ /dev/null
@@ -1,173 +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.artifacts.ivyservice;
-
-import org.apache.ivy.Ivy;
-import org.apache.ivy.core.event.EventManager;
-import org.apache.ivy.core.module.descriptor.ModuleDescriptor;
-import org.apache.ivy.core.settings.IvySettings;
-import org.apache.ivy.plugins.resolver.DependencyResolver;
-import org.gradle.api.artifacts.Configuration;
-import org.gradle.api.artifacts.Module;
-import org.gradle.api.internal.artifacts.configurations.ConfigurationInternal;
-import org.gradle.api.internal.artifacts.configurations.Configurations;
-import org.gradle.api.internal.artifacts.configurations.DependencyMetaDataProvider;
-import org.gradle.api.internal.artifacts.ivyservice.resolutionstrategy.DefaultResolutionStrategy;
-import org.gradle.api.internal.artifacts.repositories.PublicationAwareRepository;
-import org.gradle.util.JUnit4GroovyMockery;
-import org.gradle.util.WrapUtil;
-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.io.File;
-import java.io.IOException;
-import java.text.ParseException;
-import java.util.List;
-import java.util.Set;
-
-import static org.hamcrest.Matchers.equalTo;
-
-/**
- * @author Hans Dockter
- */
- at RunWith(JMock.class)
-public class IvyBackedArtifactPublisherTest {
-    private JUnit4Mockery context = new JUnit4GroovyMockery();
-
-    private ModuleDescriptor publishModuleDescriptorDummy = context.mock(ModuleDescriptor.class);
-    private ModuleDescriptor fileModuleDescriptorMock = context.mock(ModuleDescriptor.class);
-    private DependencyMetaDataProvider dependencyMetaDataProviderMock = context.mock(DependencyMetaDataProvider.class);
-    private IvyFactory ivyFactoryStub = context.mock(IvyFactory.class);
-    private SettingsConverter settingsConverterStub = context.mock(SettingsConverter.class);
-    private IvyDependencyPublisher ivyDependencyPublisherMock = context.mock(IvyDependencyPublisher.class);
-    private ModuleDescriptorConverter publishModuleDescriptorConverter = context.mock(ModuleDescriptorConverter.class, "publishConverter");
-    private ModuleDescriptorConverter fileModuleDescriptorConverter = context.mock(ModuleDescriptorConverter.class, "fileConverter");
-    private DependencyResolver resolver1 = context.mock(DependencyResolver.class);
-    private DependencyResolver resolver2 = context.mock(DependencyResolver.class);
-    private PublicationAwareRepository repo1 = repo(resolver1);
-    private PublicationAwareRepository repo2 = repo(resolver2);
-    final List<DependencyResolver> publishResolversDummy = WrapUtil.toList(resolver1, resolver2);
-    final List<PublicationAwareRepository> publishRepositoriesDummy = WrapUtil.toList(repo1, repo2);
-
-    @Test
-    public void testPublish() throws IOException, ParseException {
-        final IvySettings ivySettingsDummy = new IvySettings();
-        final EventManager ivyEventManagerDummy = new EventManager();
-        final ConfigurationInternal configuration = context.mock(ConfigurationInternal.class);
-        final Set<Configuration> configurations = createConfiguration();
-        final File someDescriptorDestination = new File("somePath");
-        final Module moduleDummy = context.mock(Module.class, "moduleForResolve");
-        final IvyBackedArtifactPublisher ivyService = createIvyService();
-
-        setUpIvyFactory(ivySettingsDummy, ivyEventManagerDummy);
-        setUpForPublish(configurations, publishResolversDummy, moduleDummy, ivySettingsDummy);
-
-        final Set<String> expectedConfigurations = Configurations.getNames(configurations, true);
-        context.checking(new Expectations() {{
-            allowing(configuration).getHierarchy();
-            will(returnValue(configurations));
-            allowing(configuration).getModule();
-            will(returnValue(moduleDummy));
-            allowing(configuration).getResolutionStrategy();
-            will(returnValue(new DefaultResolutionStrategy()));
-            one(ivyDependencyPublisherMock).publish(expectedConfigurations,
-                    publishResolversDummy, publishModuleDescriptorDummy, someDescriptorDestination, ivyEventManagerDummy);
-        }});
-
-        ivyService.publish(publishRepositoriesDummy, configuration.getModule(), configuration.getHierarchy(), someDescriptorDestination);
-    }
-
-    private IvyBackedArtifactPublisher createIvyService() {
-        return new IvyBackedArtifactPublisher(
-                settingsConverterStub,
-                publishModuleDescriptorConverter,
-                ivyFactoryStub,
-                ivyDependencyPublisherMock);
-    }
-
-    private Set<Configuration> createConfiguration() {
-        final Configuration configurationStub1 = context.mock(Configuration.class, "confStub1");
-        final Configuration configurationStub2 = context.mock(Configuration.class, "confStub2");
-        context.checking(new Expectations() {{
-            allowing(configurationStub1).getName();
-            will(returnValue("conf1"));
-
-            allowing(configurationStub1).getHierarchy();
-            will(returnValue(WrapUtil.toLinkedSet(configurationStub1)));
-
-            allowing(configurationStub1).getAll();
-            will(returnValue(WrapUtil.toLinkedSet(configurationStub1, configurationStub2)));
-
-            allowing(configurationStub2).getName();
-            will(returnValue("conf2"));
-
-            allowing(configurationStub2).getHierarchy();
-            will(returnValue(WrapUtil.toLinkedSet(configurationStub2)));
-
-            allowing(configurationStub2).getAll();
-            will(returnValue(WrapUtil.toLinkedSet(configurationStub1, configurationStub2)));
-        }});
-        return WrapUtil.toSet(configurationStub1, configurationStub2);
-    }
-
-    private void setUpForPublish(final Set<Configuration> configurations,
-                                 final List<DependencyResolver> publishResolversDummy, final Module moduleDummy,
-                                 final IvySettings ivySettingsDummy) {
-        context.checking(new Expectations() {{
-            allowing(dependencyMetaDataProviderMock).getModule();
-            will(returnValue(moduleDummy));
-
-            allowing(settingsConverterStub).convertForPublish(publishResolversDummy);
-            will(returnValue(ivySettingsDummy));
-
-            allowing(publishModuleDescriptorConverter).convert(with(equalTo(configurations)),
-                    with(equalTo(moduleDummy)));
-            will(returnValue(publishModuleDescriptorDummy));
-
-            allowing(fileModuleDescriptorConverter).convert(with(equalTo(configurations)),
-                    with(equalTo(moduleDummy)));
-            will(returnValue(fileModuleDescriptorMock));
-
-        }});
-    }
-
-    private Ivy setUpIvyFactory(final IvySettings ivySettingsDummy, final EventManager ivyEventManagerDummy) {
-        final Ivy ivyStub = context.mock(Ivy.class);
-        context.checking(new Expectations() {{
-            allowing(ivyFactoryStub).createIvy(ivySettingsDummy);
-            will(returnValue(ivyStub));
-
-            allowing(ivyStub).getSettings();
-            will(returnValue(ivySettingsDummy));
-
-            allowing(ivyStub).getEventManager();
-            will(returnValue(ivyEventManagerDummy));
-        }});
-        return ivyStub;
-    }
-
-    private PublicationAwareRepository repo(final DependencyResolver resolver) {
-        final PublicationAwareRepository repository = context.mock(PublicationAwareRepository.class);
-        context.checking(new Expectations() {{
-            one(repository).createPublisher();
-            will(returnValue(resolver));
-        }});
-        return repository;
-    }
-}
diff --git a/subprojects/core-impl/src/test/groovy/org/gradle/api/internal/artifacts/ivyservice/IvySettingsFactoryTest.groovy b/subprojects/core-impl/src/test/groovy/org/gradle/api/internal/artifacts/ivyservice/IvySettingsFactoryTest.groovy
deleted file mode 100644
index b0cbbdc..0000000
--- a/subprojects/core-impl/src/test/groovy/org/gradle/api/internal/artifacts/ivyservice/IvySettingsFactoryTest.groovy
+++ /dev/null
@@ -1,37 +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.api.internal.artifacts.ivyservice
-
-import org.gradle.api.artifacts.ArtifactRepositoryContainer
-import spock.lang.Specification
-
-class IvySettingsFactoryTest extends Specification {
-    final File cacheDir = new File('user-dir')
-    final ArtifactCacheMetaData cacheMetaData = Mock()
-    final IvySettingsFactory factory = new IvySettingsFactory(cacheMetaData)
-
-    def "creates and configures an IvySettings instance"() {
-        given:
-        _ * cacheMetaData.cacheDir >> cacheDir
-
-        when:
-        def settings = factory.create()
-
-        then:
-        settings.defaultCache == new File(cacheDir, 'ivy')
-        settings.defaultCacheArtifactPattern == ArtifactRepositoryContainer.DEFAULT_CACHE_ARTIFACT_PATTERN
-    }
-}
diff --git a/subprojects/core-impl/src/test/groovy/org/gradle/api/internal/artifacts/ivyservice/IvyUtilTest.groovy b/subprojects/core-impl/src/test/groovy/org/gradle/api/internal/artifacts/ivyservice/IvyUtilTest.groovy
index 6dbe063..d865d7c 100644
--- a/subprojects/core-impl/src/test/groovy/org/gradle/api/internal/artifacts/ivyservice/IvyUtilTest.groovy
+++ b/subprojects/core-impl/src/test/groovy/org/gradle/api/internal/artifacts/ivyservice/IvyUtilTest.groovy
@@ -18,11 +18,9 @@ package org.gradle.api.internal.artifacts.ivyservice
 
 import org.apache.ivy.core.module.id.ModuleRevisionId
 import org.junit.Test
+
 import static org.junit.Assert.assertEquals
 
-/**
- * @author Hans Dockter
- */
 class IvyUtilTest {
     @Test public void testModuleRevisionId() {
         List l = ['myorg', 'myname', 'myrev']
diff --git a/subprojects/core-impl/src/test/groovy/org/gradle/api/internal/artifacts/ivyservice/ModuleVersionNotFoundExceptionTest.groovy b/subprojects/core-impl/src/test/groovy/org/gradle/api/internal/artifacts/ivyservice/ModuleVersionNotFoundExceptionTest.groovy
index 2de2301..7939b7b 100644
--- a/subprojects/core-impl/src/test/groovy/org/gradle/api/internal/artifacts/ivyservice/ModuleVersionNotFoundExceptionTest.groovy
+++ b/subprojects/core-impl/src/test/groovy/org/gradle/api/internal/artifacts/ivyservice/ModuleVersionNotFoundExceptionTest.groovy
@@ -34,9 +34,9 @@ class ModuleVersionNotFoundExceptionTest extends Specification {
     }
 
     def "can add incoming paths to exception"() {
-        def a = newInstance("org", "a", "1.2")
-        def b = newInstance("org", "b", "5")
-        def c = newInstance("org", "c", "1.0")
+        def a = newId("org", "a", "1.2")
+        def b = newId("org", "b", "5")
+        def c = newId("org", "c", "1.0")
 
         def exception = new ModuleVersionNotFoundException(newInstance("a", "b", "c"))
         def onePath = exception.withIncomingPaths([[a, b, c]])
diff --git a/subprojects/core-impl/src/test/groovy/org/gradle/api/internal/artifacts/ivyservice/ModuleVersionResolveExceptionTest.groovy b/subprojects/core-impl/src/test/groovy/org/gradle/api/internal/artifacts/ivyservice/ModuleVersionResolveExceptionTest.groovy
index 6cbeda6..fab33e6 100644
--- a/subprojects/core-impl/src/test/groovy/org/gradle/api/internal/artifacts/ivyservice/ModuleVersionResolveExceptionTest.groovy
+++ b/subprojects/core-impl/src/test/groovy/org/gradle/api/internal/artifacts/ivyservice/ModuleVersionResolveExceptionTest.groovy
@@ -37,9 +37,9 @@ class ModuleVersionResolveExceptionTest extends Specification {
     }
 
     def "can add incoming paths to exception"() {
-        def a = newInstance("org", "a", "1.2")
-        def b = newInstance("org", "b", "5")
-        def c = newInstance("org", "c", "1.0")
+        def a = newId("org", "a", "1.2")
+        def b = newId("org", "b", "5")
+        def c = newId("org", "c", "1.0")
 
         def cause = new RuntimeException()
         def exception = new ModuleVersionResolveException(newInstance("a", "b", "c"), cause)
diff --git a/subprojects/core-impl/src/test/groovy/org/gradle/api/internal/artifacts/ivyservice/ResolvedArtifactFactoryTest.groovy b/subprojects/core-impl/src/test/groovy/org/gradle/api/internal/artifacts/ivyservice/ResolvedArtifactFactoryTest.groovy
index 243015e..14ce8ae 100644
--- a/subprojects/core-impl/src/test/groovy/org/gradle/api/internal/artifacts/ivyservice/ResolvedArtifactFactoryTest.groovy
+++ b/subprojects/core-impl/src/test/groovy/org/gradle/api/internal/artifacts/ivyservice/ResolvedArtifactFactoryTest.groovy
@@ -15,39 +15,36 @@
  */
 package org.gradle.api.internal.artifacts.ivyservice
 
+import org.apache.ivy.Ivy
 import org.apache.ivy.core.module.descriptor.Artifact
-import org.gradle.api.artifacts.ResolvedArtifact
-import org.gradle.api.artifacts.ResolvedDependency
+import org.gradle.api.Transformer
 import org.gradle.internal.Factory
-import org.gradle.api.internal.artifacts.DefaultResolvedArtifact
 import spock.lang.Specification
 
 class ResolvedArtifactFactoryTest extends Specification {
     final CacheLockingManager lockingManager = Mock()
-    final ResolvedArtifactFactory factory = new ResolvedArtifactFactory(lockingManager)
+    final IvyContextManager ivyContextManager = Mock()
+    final ResolvedArtifactFactory factory = new ResolvedArtifactFactory(lockingManager, ivyContextManager)
 
-    def "creates an artifact backed by module resolve result"() {
+    def "provides artifact source"() {
         Artifact artifact = Mock()
         ArtifactResolver artifactResolver = Mock()
-        ResolvedDependency resolvedDependency = Mock()
         File file = new File("something.jar")
 
         given:
         artifact.qualifiedExtraAttributes >> [:]
 
         when:
-        ResolvedArtifact resolvedArtifact = factory.create(resolvedDependency, artifact, artifactResolver)
-
-        then:
-        resolvedArtifact instanceof DefaultResolvedArtifact
-
-        when:
-        resolvedArtifact.file
+        File f = factory.artifactSource(artifact, artifactResolver).create()
 
         then:
+        f == file
         1 * lockingManager.useCache(!null, !null) >> {String displayName, Factory<?> action ->
             return action.create()
         }
+        1 * ivyContextManager.withIvy(!null) >> {Transformer action ->
+            return action.transform(Stub(Ivy))
+        }
         1 * artifactResolver.resolve(artifact, _) >> { args -> args[1].resolved(file) }
         0 * _._
     }
diff --git a/subprojects/core-impl/src/test/groovy/org/gradle/api/internal/artifacts/ivyservice/clientmodule/ClientModuleResolverTest.groovy b/subprojects/core-impl/src/test/groovy/org/gradle/api/internal/artifacts/ivyservice/clientmodule/ClientModuleResolverTest.groovy
index dd8b5df..837eee6 100644
--- a/subprojects/core-impl/src/test/groovy/org/gradle/api/internal/artifacts/ivyservice/clientmodule/ClientModuleResolverTest.groovy
+++ b/subprojects/core-impl/src/test/groovy/org/gradle/api/internal/artifacts/ivyservice/clientmodule/ClientModuleResolverTest.groovy
@@ -1,5 +1,5 @@
 /*
- * Copyright 2007-2009 the original author or authors.
+ * Copyright 2013 the original author or authors.
  *
  * Licensed under the Apache License, Version 2.0 (the "License");
  * you may not use this file except in compliance with the License.
@@ -21,7 +21,7 @@ import org.apache.ivy.core.module.descriptor.ModuleDescriptor
 import org.apache.ivy.core.module.id.ModuleId
 import org.apache.ivy.core.module.id.ModuleRevisionId
 import org.gradle.api.internal.artifacts.ivyservice.BuildableModuleVersionResolveResult
-import org.gradle.api.internal.artifacts.ivyservice.DependencyToModuleResolver
+import org.gradle.api.internal.artifacts.ivyservice.DependencyToModuleVersionResolver
 import org.gradle.api.internal.artifacts.ivyservice.ModuleVersionResolveException
 import org.gradle.api.internal.artifacts.ivyservice.ivyresolve.DependencyMetaData
 import org.gradle.api.internal.artifacts.ivyservice.moduleconverter.dependencies.ClientModuleDependencyDescriptor
@@ -29,13 +29,10 @@ import spock.lang.Specification
 
 import static org.gradle.api.internal.artifacts.DefaultModuleVersionSelector.newSelector
 
-/**
- * @author Hans Dockter
- */
 class ClientModuleResolverTest extends Specification {
     final ModuleDescriptor module = Mock()
     final ModuleRevisionId moduleId = new ModuleRevisionId(new ModuleId("org", "name"), "1.0")
-    final DependencyToModuleResolver target = Mock()
+    final DependencyToModuleVersionResolver target = Mock()
     final ClientModuleResolver resolver = new ClientModuleResolver(target)
 
     def "replaces meta-data for a client module dependency"() {
diff --git a/subprojects/core-impl/src/test/groovy/org/gradle/api/internal/artifacts/ivyservice/ivyresolve/CachingModuleVersionRepositoryTest.groovy b/subprojects/core-impl/src/test/groovy/org/gradle/api/internal/artifacts/ivyservice/ivyresolve/CachingModuleVersionRepositoryTest.groovy
index 81d2d7a..dac2e6e 100644
--- a/subprojects/core-impl/src/test/groovy/org/gradle/api/internal/artifacts/ivyservice/ivyresolve/CachingModuleVersionRepositoryTest.groovy
+++ b/subprojects/core-impl/src/test/groovy/org/gradle/api/internal/artifacts/ivyservice/ivyresolve/CachingModuleVersionRepositoryTest.groovy
@@ -16,13 +16,10 @@
 
 package org.gradle.api.internal.artifacts.ivyservice.ivyresolve
 
-import org.apache.ivy.core.module.descriptor.Artifact
-import org.apache.ivy.core.module.id.ArtifactId
-import org.apache.ivy.core.module.id.ArtifactRevisionId
-import org.apache.ivy.core.module.id.ModuleId
-import org.apache.ivy.core.module.id.ModuleRevisionId
+import org.gradle.api.artifacts.ArtifactIdentifier
 import org.gradle.api.internal.artifacts.configurations.dynamicversion.CachePolicy
 import org.gradle.api.internal.artifacts.ivyservice.BuildableArtifactResolveResult
+import org.gradle.api.internal.artifacts.ivyservice.DependencyToModuleVersionResolver
 import org.gradle.api.internal.artifacts.ivyservice.dynamicversions.ModuleResolutionCache
 import org.gradle.api.internal.artifacts.ivyservice.modulecache.ModuleDescriptorCache
 import org.gradle.api.internal.externalresource.cached.CachedArtifactIndex
@@ -35,13 +32,14 @@ import spock.lang.Unroll
 
 class CachingModuleVersionRepositoryTest extends Specification {
 
-    ModuleVersionRepository realRepo = Mock()
-    ModuleResolutionCache moduleResolutionCache = Mock()
-    ModuleDescriptorCache moduleDescriptorCache = Mock()
-    CachedArtifactIndex artifactAtRepositoryCache = Mock()
-    CachePolicy cachePolicy = Mock()
-    CachingModuleVersionRepository repo = new CachingModuleVersionRepository(realRepo, moduleResolutionCache, moduleDescriptorCache, artifactAtRepositoryCache, cachePolicy, new TrueTimeProvider())
-    ModuleRevisionId moduleRevisionId = Mock()
+    final realRepo = Mock(ModuleVersionRepository)
+    final moduleResolutionCache = Mock(ModuleResolutionCache)
+    final moduleDescriptorCache = Mock(ModuleDescriptorCache)
+    final artifactAtRepositoryCache = Mock(CachedArtifactIndex)
+    final resolver = Mock(DependencyToModuleVersionResolver)
+    final cachePolicy = Mock(CachePolicy)
+    CachingModuleVersionRepository repo = new CachingModuleVersionRepository(realRepo, moduleResolutionCache, moduleDescriptorCache, artifactAtRepositoryCache,
+            resolver, cachePolicy, new TrueTimeProvider())
     int descriptorHash = 1234
     CachingModuleVersionRepository.CachingModuleSource moduleSource = Mock()
 
@@ -51,12 +49,11 @@ class CachingModuleVersionRepositoryTest extends Specification {
         ExternalResourceMetaData externalResourceMetaData = new DefaultExternalResourceMetaData("remote url", lastModified, -1, null, null)
         File file = new File("local")
         BuildableArtifactResolveResult result = Mock()
-        Artifact artifact = Mock()
-        ArtifactRevisionId id = arid()
-        ArtifactAtRepositoryKey atRepositoryKey = new ArtifactAtRepositoryKey(realRepo, id)
+        ArtifactIdentifier artifact = Stub()
+        ArtifactAtRepositoryKey atRepositoryKey = new ArtifactAtRepositoryKey("repo-id", artifact)
 
         and:
-        _ * artifact.getModuleRevisionId() >> moduleRevisionId;
+        _ * realRepo.id >> "repo-id"
         _ * realRepo.isLocal() >> false
         _ * moduleSource.descriptorHash >> descriptorHash
         _ * moduleSource.isChangingModule >> true
@@ -64,7 +61,6 @@ class CachingModuleVersionRepositoryTest extends Specification {
         _ * realRepo.resolve(artifact, result, null)
         _ * result.file >> file
         _ * result.externalResourceMetaData >> externalResourceMetaData
-        _ * artifact.getId() >> id
 
         when:
         repo.resolve(artifact, result, moduleSource)
@@ -75,20 +71,4 @@ class CachingModuleVersionRepositoryTest extends Specification {
         where:
         lastModified << [new Date(), null]
     }
-
-    ArtifactRevisionId arid(Map attrs = [:]) {
-        Map defaults = [
-                org: "org", name: "name", revision: "1.0",
-                type: "type", ext: "ext"
-        ]
-
-        attrs = defaults + attrs
-
-        ModuleId mid = new ModuleId(attrs.org, attrs.name)
-        new ArtifactRevisionId(
-                new ArtifactId(mid, mid.name, attrs.type, attrs.ext),
-                new ModuleRevisionId(mid, attrs.revision)
-        )
-    }
-
 }
diff --git a/subprojects/core-impl/src/test/groovy/org/gradle/api/internal/artifacts/ivyservice/ivyresolve/DefaultBuildableModuleVersionMetaDataResolveResultTest.groovy b/subprojects/core-impl/src/test/groovy/org/gradle/api/internal/artifacts/ivyservice/ivyresolve/DefaultBuildableModuleVersionMetaDataResolveResultTest.groovy
new file mode 100644
index 0000000..925e0aa
--- /dev/null
+++ b/subprojects/core-impl/src/test/groovy/org/gradle/api/internal/artifacts/ivyservice/ivyresolve/DefaultBuildableModuleVersionMetaDataResolveResultTest.groovy
@@ -0,0 +1,428 @@
+/*
+ * Copyright 2012 the original author or authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY 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.ivyresolve
+
+import org.apache.ivy.core.module.descriptor.*
+import org.apache.ivy.core.module.id.ModuleRevisionId
+import org.gradle.api.artifacts.ModuleVersionIdentifier
+import org.gradle.api.internal.artifacts.ivyservice.ModuleVersionResolveException
+import spock.lang.Specification
+
+import static org.gradle.api.internal.artifacts.DefaultModuleVersionSelector.newSelector
+
+class DefaultBuildableModuleVersionMetaDataResolveResultTest extends Specification {
+    final DefaultBuildableModuleVersionMetaDataResolveResult descriptor = new DefaultBuildableModuleVersionMetaDataResolveResult()
+    ModuleSource moduleSource = Stub()
+
+    def "has unknown state by default"() {
+        expect:
+        descriptor.state == BuildableModuleVersionMetaDataResolveResult.State.Unknown
+    }
+
+    def "can mark as missing"() {
+        when:
+        descriptor.missing()
+
+        then:
+        descriptor.state == BuildableModuleVersionMetaDataResolveResult.State.Missing
+        descriptor.failure == null
+    }
+
+    def "can mark as probably missing"() {
+        when:
+        descriptor.probablyMissing()
+
+        then:
+        descriptor.state == BuildableModuleVersionMetaDataResolveResult.State.ProbablyMissing
+        descriptor.failure == null
+    }
+
+    def "can mark as failed"() {
+        def failure = new ModuleVersionResolveException(newSelector("a", "b", "c"), "broken")
+
+        when:
+        descriptor.failed(failure)
+
+        then:
+        descriptor.state == BuildableModuleVersionMetaDataResolveResult.State.Failed
+        descriptor.failure == failure
+    }
+
+    def "can mark as resolved"() {
+        def id = Stub(ModuleVersionIdentifier)
+        def moduleDescriptor = Stub(ModuleDescriptor)
+
+        when:
+        descriptor.resolved(id, moduleDescriptor, true, moduleSource)
+
+        then:
+        descriptor.state == BuildableModuleVersionMetaDataResolveResult.State.Resolved
+        descriptor.failure == null
+        descriptor.id == id
+        descriptor.descriptor == moduleDescriptor
+        descriptor.changing
+        descriptor.moduleSource == moduleSource
+    }
+
+    def "builds and caches the dependency meta-data from the module descriptor"() {
+        def id = Stub(ModuleVersionIdentifier)
+        def moduleDescriptor = Mock(ModuleDescriptor)
+        def dependency1 = new DefaultDependencyDescriptor(ModuleRevisionId.newInstance("org", "module", "1.2"), false)
+        def dependency2 = new DefaultDependencyDescriptor(ModuleRevisionId.newInstance("org", "module", "1.2"), false)
+
+        given:
+        moduleDescriptor.dependencies >> ([dependency1, dependency2] as DependencyDescriptor[])
+
+        and:
+        descriptor.resolved(id, moduleDescriptor, true, moduleSource)
+
+        when:
+        def deps = descriptor.dependencies
+
+        then:
+        deps.size() == 2
+        deps[0].descriptor == dependency1
+        deps[1].descriptor == dependency2
+
+        when:
+        def deps2 = descriptor.dependencies
+
+        then:
+        deps2.is(deps)
+
+        and:
+        0 * moduleDescriptor._
+    }
+
+    def "builds and caches the configuration meta-data from the module descriptor"() {
+        def id = Stub(ModuleVersionIdentifier)
+        def moduleDescriptor = Mock(ModuleDescriptor)
+
+        given:
+        descriptor.resolved(id, moduleDescriptor, true, moduleSource)
+
+        when:
+        def config = descriptor.getConfiguration("conf")
+
+        then:
+        1 * moduleDescriptor.getConfiguration("conf") >> Stub(Configuration)
+
+        when:
+        def config2 = descriptor.getConfiguration("conf")
+
+        then:
+        config2.is(config)
+
+        and:
+        0 * moduleDescriptor._
+    }
+
+    def "returns null for unknown configuration"() {
+        def id = Stub(ModuleVersionIdentifier)
+        def moduleDescriptor = Mock(ModuleDescriptor)
+
+        given:
+        moduleDescriptor.getConfiguration("conf") >> null
+
+        and:
+        descriptor.resolved(id, moduleDescriptor, true, moduleSource)
+
+        expect:
+        descriptor.getConfiguration("conf") == null
+    }
+
+    def "builds and caches dependencies for a configuration"() {
+        def id = Stub(ModuleVersionIdentifier)
+        def moduleDescriptor = Stub(ModuleDescriptor)
+        def config = Stub(Configuration)
+        def dependency1 = new DefaultDependencyDescriptor(ModuleRevisionId.newInstance("org", "module", "1.2"), false)
+        dependency1.addDependencyConfiguration("conf", "a")
+        def dependency2 = new DefaultDependencyDescriptor(ModuleRevisionId.newInstance("org", "module", "1.2"), false)
+        dependency2.addDependencyConfiguration("*", "b")
+        def dependency3 = new DefaultDependencyDescriptor(ModuleRevisionId.newInstance("org", "module", "1.2"), false)
+        dependency3.addDependencyConfiguration("super", "c")
+        def dependency4 = new DefaultDependencyDescriptor(ModuleRevisionId.newInstance("org", "module", "1.2"), false)
+        dependency4.addDependencyConfiguration("other", "d")
+        def dependency5 = new DefaultDependencyDescriptor(ModuleRevisionId.newInstance("org", "module", "1.2"), false)
+        dependency5.addDependencyConfiguration("%", "e")
+
+        given:
+        moduleDescriptor.dependencies >> ([dependency1, dependency2, dependency3, dependency4, dependency5] as DependencyDescriptor[])
+        moduleDescriptor.getConfiguration("conf") >> config
+        config.extends >> ["super"]
+
+        and:
+        descriptor.resolved(id, moduleDescriptor, true, moduleSource)
+
+        when:
+        def dependencies = descriptor.getConfiguration("conf").dependencies
+
+        then:
+        dependencies*.descriptor == [dependency1, dependency2, dependency3, dependency5]
+
+        and:
+        descriptor.getConfiguration("conf").dependencies.is(dependencies)
+
+        when:
+        descriptor.setDependencies([])
+
+        then:
+        descriptor.getConfiguration("conf").dependencies == []
+    }
+
+    def "builds and caches artifacts from the module descriptor"() {
+        def id = Stub(ModuleVersionIdentifier)
+        def moduleDescriptor = new DefaultModuleDescriptor(ModuleRevisionId.newInstance("org", "group", "version"), "status", null)
+        def artifact1 = Stub(Artifact)
+        def artifact2 = Stub(Artifact)
+
+        given:
+        moduleDescriptor.addConfiguration(new Configuration("config"))
+        moduleDescriptor.addArtifact("config", artifact1)
+        moduleDescriptor.addArtifact("config", artifact2)
+
+        and:
+        descriptor.resolved(id, moduleDescriptor, true, moduleSource)
+
+        when:
+        def artifacts = descriptor.getConfiguration("config").artifacts
+
+        then:
+        artifacts as List == [artifact1, artifact2]
+
+        and:
+        descriptor.getConfiguration("config").artifacts.is(artifacts)
+    }
+
+    def "artifacts include those inherited from other configurations"() {
+        def id = Stub(ModuleVersionIdentifier)
+        def moduleDescriptor = new DefaultModuleDescriptor(ModuleRevisionId.newInstance("org", "group", "version"), "status", null)
+        def artifact1 = Stub(Artifact)
+        def artifact2 = Stub(Artifact)
+        def artifact3 = Stub(Artifact)
+
+        given:
+        moduleDescriptor.addConfiguration(new Configuration("super"))
+        moduleDescriptor.addConfiguration(new Configuration("config", Configuration.Visibility.PUBLIC, "", ["super"] as String[], true, null))
+        moduleDescriptor.addArtifact("super", artifact1)
+        moduleDescriptor.addArtifact("super", artifact2)
+        moduleDescriptor.addArtifact("config", artifact2)
+        moduleDescriptor.addArtifact("config", artifact3)
+
+        and:
+        descriptor.resolved(id, moduleDescriptor, true, moduleSource)
+
+        when:
+        def artifacts = descriptor.getConfiguration("config").artifacts
+
+        then:
+        artifacts as List == [artifact2, artifact3, artifact1]
+    }
+
+    def "builds and caches exclude rules for a configuration"() {
+        def id = Stub(ModuleVersionIdentifier)
+        def moduleDescriptor = new DefaultModuleDescriptor(ModuleRevisionId.newInstance("org", "group", "version"), "status", null)
+        def rule1 = Stub(ExcludeRule)
+        def rule2 = Stub(ExcludeRule)
+        def rule3 = Stub(ExcludeRule)
+
+        given:
+        rule1.configurations >> ["config"]
+        rule2.configurations >> ["super"]
+        rule3.configurations >> ["other"]
+
+        and:
+        moduleDescriptor.addConfiguration(new Configuration("super"))
+        moduleDescriptor.addConfiguration(new Configuration("config", Configuration.Visibility.PUBLIC, "", ["super"] as String[], true, null))
+        moduleDescriptor.addExcludeRule(rule1)
+        moduleDescriptor.addExcludeRule(rule2)
+        moduleDescriptor.addExcludeRule(rule3)
+
+        and:
+        descriptor.resolved(id, moduleDescriptor, true, moduleSource)
+
+        when:
+        def excludeRules = descriptor.getConfiguration("config").excludeRules
+
+        then:
+        excludeRules as List == [rule1, rule2]
+
+        and:
+        descriptor.getConfiguration("config").excludeRules.is(excludeRules)
+    }
+
+    def "can replace the dependencies for the module version"() {
+        def id = Stub(ModuleVersionIdentifier)
+        def moduleDescriptor = Mock(ModuleDescriptor)
+        def dependency1 = Stub(DependencyMetaData)
+        def dependency2 = Stub(DependencyMetaData)
+
+        given:
+        descriptor.resolved(id, moduleDescriptor, true, moduleSource)
+
+        when:
+        descriptor.dependencies = [dependency1, dependency2]
+
+        then:
+        descriptor.dependencies == [dependency1, dependency2]
+
+        and:
+        0 * moduleDescriptor._
+    }
+
+    def "cannot get failure when not resolved"() {
+        when:
+        descriptor.failure
+
+        then:
+        thrown(IllegalStateException)
+    }
+
+    def "cannot get meta-data when not resolved"() {
+        when:
+        descriptor.metaData
+
+        then:
+        thrown(IllegalStateException)
+    }
+
+    def "cannot get meta-data when failed"() {
+        given:
+        def failure = new ModuleVersionResolveException(newSelector("a", "b", "c"), "broken")
+        descriptor.failed(failure)
+
+        when:
+        descriptor.metaData
+
+        then:
+        ModuleVersionResolveException e = thrown()
+        e == failure
+    }
+
+    def "cannot get descriptor when not resolved"() {
+        when:
+        descriptor.descriptor
+
+        then:
+        thrown(IllegalStateException)
+    }
+
+    def "cannot get descriptor when failed"() {
+        given:
+        def failure = new ModuleVersionResolveException(newSelector("a", "b", "c"), "broken")
+        descriptor.failed(failure)
+
+        when:
+        descriptor.descriptor
+
+        then:
+        ModuleVersionResolveException e = thrown()
+        e == failure
+    }
+
+    def "cannot get descriptor when missing"() {
+        given:
+        descriptor.missing()
+
+        when:
+        descriptor.descriptor
+
+        then:
+        thrown(IllegalStateException)
+    }
+
+    def "cannot get descriptor when probably missing"() {
+        given:
+        descriptor.probablyMissing()
+
+        when:
+        descriptor.descriptor
+
+        then:
+        thrown(IllegalStateException)
+    }
+
+    def "cannot get module source when failed"() {
+        given:
+        def failure = new ModuleVersionResolveException(newSelector("a", "b", "c"), "broken")
+        descriptor.failed(failure)
+
+        when:
+        descriptor.getModuleSource()
+
+        then:
+        ModuleVersionResolveException e = thrown()
+        e == failure
+    }
+
+    def "cannot set module source when failed"() {
+        given:
+        def failure = new ModuleVersionResolveException(newSelector("a", "b", "c"), "broken")
+        descriptor.failed(failure)
+
+        when:
+        descriptor.setModuleSource(Mock(ModuleSource))
+
+        then:
+        ModuleVersionResolveException e = thrown()
+        e == failure
+    }
+
+    def "cannot get module source when missing"() {
+        given:
+        descriptor.missing()
+
+        when:
+        descriptor.getModuleSource()
+
+        then:
+        thrown(IllegalStateException)
+    }
+
+    def "cannot set module source when missing"() {
+        given:
+        descriptor.missing()
+
+        when:
+        descriptor.setModuleSource(Mock(ModuleSource))
+
+        then:
+        thrown(IllegalStateException)
+    }
+
+    def "cannot get module source when probably missing"() {
+        given:
+        descriptor.probablyMissing()
+
+        when:
+        descriptor.getModuleSource()
+
+        then:
+        thrown(IllegalStateException)
+    }
+
+    def "cannot set module source when probably missing"() {
+        given:
+        descriptor.probablyMissing()
+
+        when:
+        descriptor.setModuleSource(Mock(ModuleSource))
+
+        then:
+        thrown(IllegalStateException)
+    }
+}
diff --git a/subprojects/core-impl/src/test/groovy/org/gradle/api/internal/artifacts/ivyservice/ivyresolve/DefaultBuildableModuleVersionMetaDataTest.groovy b/subprojects/core-impl/src/test/groovy/org/gradle/api/internal/artifacts/ivyservice/ivyresolve/DefaultBuildableModuleVersionMetaDataTest.groovy
deleted file mode 100644
index df18c01..0000000
--- a/subprojects/core-impl/src/test/groovy/org/gradle/api/internal/artifacts/ivyservice/ivyresolve/DefaultBuildableModuleVersionMetaDataTest.groovy
+++ /dev/null
@@ -1,244 +0,0 @@
-/*
- * Copyright 2012 the original author or authors.
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- *      http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY 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.ivyresolve
-
-import org.apache.ivy.core.module.descriptor.DefaultDependencyDescriptor
-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.gradle.api.artifacts.ModuleVersionIdentifier
-import org.gradle.api.internal.artifacts.ivyservice.ModuleVersionResolveException
-import spock.lang.Specification
-
-import static org.gradle.api.internal.artifacts.DefaultModuleVersionSelector.newSelector
-
-class DefaultBuildableModuleVersionMetaDataTest extends Specification {
-    final DefaultBuildableModuleVersionMetaData descriptor = new DefaultBuildableModuleVersionMetaData()
-    ModuleSource moduleSource = Mock()
-
-    def "has unknown state by default"() {
-        expect:
-        descriptor.state == BuildableModuleVersionMetaData.State.Unknown
-    }
-
-    def "can mark as missing"() {
-        when:
-        descriptor.missing()
-
-        then:
-        descriptor.state == BuildableModuleVersionMetaData.State.Missing
-        descriptor.failure == null
-    }
-
-    def "can mark as probably missing"() {
-        when:
-        descriptor.probablyMissing()
-
-        then:
-        descriptor.state == BuildableModuleVersionMetaData.State.ProbablyMissing
-        descriptor.failure == null
-    }
-
-    def "can mark as failed"() {
-        def failure = new ModuleVersionResolveException(newSelector("a", "b", "c"), "broken")
-
-        when:
-        descriptor.failed(failure)
-
-        then:
-        descriptor.state == BuildableModuleVersionMetaData.State.Failed
-        descriptor.failure == failure
-    }
-
-    def "can mark as resolved"() {
-        def id = Mock(ModuleVersionIdentifier)
-        def moduleDescriptor = Mock(ModuleDescriptor)
-
-        when:
-        descriptor.resolved(id, moduleDescriptor, true, moduleSource)
-
-        then:
-        descriptor.state == BuildableModuleVersionMetaData.State.Resolved
-        descriptor.failure == null
-        descriptor.id == id
-        descriptor.descriptor == moduleDescriptor
-        descriptor.changing
-        descriptor.moduleSource == moduleSource
-    }
-
-    def "builds and caches the dependency meta-data from the module descriptor"() {
-        def id = Mock(ModuleVersionIdentifier)
-        def moduleDescriptor = Mock(ModuleDescriptor)
-        def dependency1 = new DefaultDependencyDescriptor(ModuleRevisionId.newInstance("org", "module", "1.2"), false)
-        def dependency2 = new DefaultDependencyDescriptor(ModuleRevisionId.newInstance("org", "module", "1.2"), false)
-
-        given:
-        moduleDescriptor.dependencies >> ([dependency1, dependency2] as DependencyDescriptor[])
-
-        and:
-        descriptor.resolved(id, moduleDescriptor, true, moduleSource)
-
-        when:
-        def deps = descriptor.dependencies
-
-        then:
-        deps.size() == 2
-        deps[0].descriptor == dependency1
-        deps[1].descriptor == dependency2
-
-        and:
-        descriptor.dependencies.is(deps)
-    }
-
-    def "can replace the dependencies for the module version"() {
-        def id = Mock(ModuleVersionIdentifier)
-        def moduleDescriptor = Mock(ModuleDescriptor)
-        def dependency1 = Mock(DependencyMetaData)
-        def dependency2 = Mock(DependencyMetaData)
-
-        given:
-        descriptor.resolved(id, moduleDescriptor, true, moduleSource)
-
-        when:
-        descriptor.dependencies = [dependency1, dependency2]
-
-        then:
-        descriptor.dependencies == [dependency1, dependency2]
-
-        and:
-        0 * moduleDescriptor._
-    }
-
-    def "cannot get descriptor when not resolved"() {
-        when:
-        descriptor.descriptor
-
-        then:
-        thrown(IllegalStateException)
-
-        when:
-        descriptor.failure
-
-        then:
-        thrown(IllegalStateException)
-    }
-
-    def "cannot get descriptor when failed"() {
-        given:
-        def failure = new ModuleVersionResolveException(newSelector("a", "b", "c"), "broken")
-        descriptor.failed(failure)
-
-        when:
-        descriptor.descriptor
-
-        then:
-        ModuleVersionResolveException e = thrown()
-        e == failure
-    }
-
-    def "cannot get descriptor when missing"() {
-        given:
-        descriptor.missing()
-
-        when:
-        descriptor.descriptor
-
-        then:
-        thrown(IllegalStateException)
-    }
-
-    def "cannot get descriptor when probably missing"() {
-        given:
-        descriptor.probablyMissing()
-
-        when:
-        descriptor.descriptor
-
-        then:
-        thrown(IllegalStateException)
-    }
-
-    def "cannot get module source when failed"() {
-        given:
-        def failure = new ModuleVersionResolveException(newSelector("a", "b", "c"), "broken")
-        descriptor.failed(failure)
-
-        when:
-        descriptor.getModuleSource()
-
-        then:
-        ModuleVersionResolveException e = thrown()
-        e == failure
-    }
-
-    def "cannot set module source when failed"() {
-        given:
-        def failure = new ModuleVersionResolveException(newSelector("a", "b", "c"), "broken")
-        descriptor.failed(failure)
-
-        when:
-        descriptor.setModuleSource(Mock(ModuleSource))
-
-        then:
-        ModuleVersionResolveException e = thrown()
-        e == failure
-    }
-
-    def "cannot get module source when missing"() {
-        given:
-        descriptor.missing()
-
-        when:
-        descriptor.getModuleSource()
-
-        then:
-        thrown(IllegalStateException)
-    }
-
-    def "cannot set module source when missing"() {
-        given:
-        descriptor.missing()
-
-        when:
-        descriptor.setModuleSource(Mock(ModuleSource))
-
-        then:
-        thrown(IllegalStateException)
-    }
-
-    def "cannot get module source when probably missing"() {
-        given:
-        descriptor.probablyMissing()
-
-        when:
-        descriptor.getModuleSource()
-
-        then:
-        thrown(IllegalStateException)
-    }
-
-    def "cannot set module source when probably missing"() {
-        given:
-        descriptor.probablyMissing()
-
-        when:
-        descriptor.setModuleSource(Mock(ModuleSource))
-
-        then:
-        thrown(IllegalStateException)
-    }
-}
diff --git a/subprojects/core-impl/src/test/groovy/org/gradle/api/internal/artifacts/ivyservice/ivyresolve/DependencyResolverIdentifierTest.groovy b/subprojects/core-impl/src/test/groovy/org/gradle/api/internal/artifacts/ivyservice/ivyresolve/DependencyResolverIdentifierTest.groovy
index 00268c2..952294e 100644
--- a/subprojects/core-impl/src/test/groovy/org/gradle/api/internal/artifacts/ivyservice/ivyresolve/DependencyResolverIdentifierTest.groovy
+++ b/subprojects/core-impl/src/test/groovy/org/gradle/api/internal/artifacts/ivyservice/ivyresolve/DependencyResolverIdentifierTest.groovy
@@ -21,15 +21,6 @@ import org.gradle.api.internal.artifacts.repositories.resolver.ExternalResourceR
 import spock.lang.Specification
 
 public class DependencyResolverIdentifierTest extends Specification {
-    def "uses dependency resolver name"() {
-        given:
-        DependencyResolver resolver = Mock()
-        resolver.name >> "resolver-name"
-
-        expect:
-        new DependencyResolverIdentifier(resolver).name == "resolver-name"
-    }
-
     def "dependency resolvers of unknown type are identified by their name"() {
         given:
         DependencyResolver resolver1 = Mock()
@@ -123,6 +114,10 @@ public class DependencyResolverIdentifierTest extends Specification {
     }
 
     def id(DependencyResolver resolver) {
-        return new DependencyResolverIdentifier(resolver).uniqueId
+        return DependencyResolverIdentifier.forIvyResolver(resolver)
+    }
+
+    def id(ExternalResourceResolver resolver) {
+        return DependencyResolverIdentifier.forExternalResourceResolver(resolver)
     }
 }
diff --git a/subprojects/core-impl/src/test/groovy/org/gradle/api/internal/artifacts/ivyservice/ivyresolve/IvyDynamicResolveModuleVersionRepositoryTest.groovy b/subprojects/core-impl/src/test/groovy/org/gradle/api/internal/artifacts/ivyservice/ivyresolve/IvyDynamicResolveModuleVersionRepositoryTest.groovy
index 2da5d0a..c4e8377 100644
--- a/subprojects/core-impl/src/test/groovy/org/gradle/api/internal/artifacts/ivyservice/ivyresolve/IvyDynamicResolveModuleVersionRepositoryTest.groovy
+++ b/subprojects/core-impl/src/test/groovy/org/gradle/api/internal/artifacts/ivyservice/ivyresolve/IvyDynamicResolveModuleVersionRepositoryTest.groovy
@@ -22,8 +22,9 @@ import spock.lang.Specification
 
 class IvyDynamicResolveModuleVersionRepositoryTest extends Specification {
     final target = Mock(LocalAwareModuleVersionRepository)
+    final metaData = Mock(ModuleVersionMetaData)
     final requestedDependency = Mock(DependencyMetaData)
-    final result = Mock(BuildableModuleVersionMetaData)
+    final result = Mock(BuildableModuleVersionMetaDataResolveResult)
     final repository = new IvyDynamicResolveModuleVersionRepository(target)
 
     def "replaces each dependency version with revConstraint"() {
@@ -31,7 +32,8 @@ class IvyDynamicResolveModuleVersionRepositoryTest extends Specification {
         def transformed = dependency()
 
         given:
-        result.state >> BuildableModuleVersionMetaData.State.Resolved
+        result.state >> BuildableModuleVersionMetaDataResolveResult.State.Resolved
+        result.metaData >> metaData
 
         when:
         repository.getLocalDependency(requestedDependency, result)
@@ -40,7 +42,7 @@ class IvyDynamicResolveModuleVersionRepositoryTest extends Specification {
         1 * target.getLocalDependency(requestedDependency, result)
 
         and:
-        1 * result.dependencies >> [original]
+        1 * metaData.dependencies >> [original]
         1 * original.withRequestedVersion('1.2+') >> transformed
         1 * result.setDependencies([transformed])
     }
@@ -51,7 +53,7 @@ class IvyDynamicResolveModuleVersionRepositoryTest extends Specification {
 
         then:
         1 * target.getLocalDependency(requestedDependency, result)
-        _ * result.state >> BuildableModuleVersionMetaData.State.Missing
+        _ * result.state >> BuildableModuleVersionMetaDataResolveResult.State.Missing
         0 * result._
     }
 
diff --git a/subprojects/core-impl/src/test/groovy/org/gradle/api/internal/artifacts/ivyservice/ivyresolve/LazyDependencyToModuleResolverTest.groovy b/subprojects/core-impl/src/test/groovy/org/gradle/api/internal/artifacts/ivyservice/ivyresolve/LazyDependencyToModuleResolverTest.groovy
index 8828189..e53f6cb 100644
--- a/subprojects/core-impl/src/test/groovy/org/gradle/api/internal/artifacts/ivyservice/ivyresolve/LazyDependencyToModuleResolverTest.groovy
+++ b/subprojects/core-impl/src/test/groovy/org/gradle/api/internal/artifacts/ivyservice/ivyresolve/LazyDependencyToModuleResolverTest.groovy
@@ -14,25 +14,24 @@
  * limitations under the License.
  */
 package org.gradle.api.internal.artifacts.ivyservice.ivyresolve
-
 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.module.id.ModuleRevisionId
-import org.apache.ivy.plugins.version.VersionMatcher
 import org.gradle.api.artifacts.ModuleVersionSelector
 import org.gradle.api.internal.artifacts.DefaultModuleVersionIdentifier
 import org.gradle.api.internal.artifacts.DefaultModuleVersionSelector
 import org.gradle.api.internal.artifacts.ivyservice.*
+import org.gradle.api.internal.artifacts.ivyservice.ivyresolve.strategy.VersionMatcher
 import spock.lang.Specification
 
-import static org.gradle.api.internal.artifacts.DefaultModuleVersionSelector.newSelector
 import static org.gradle.api.internal.artifacts.DefaultModuleVersionIdentifier.newId
+import static org.gradle.api.internal.artifacts.DefaultModuleVersionSelector.newSelector
 
 class LazyDependencyToModuleResolverTest extends Specification {
-    final DependencyToModuleResolver target = Mock()
-    final VersionMatcher matcher = Mock()
+    final target = Mock(DependencyToModuleVersionResolver)
+    final matcher = Mock(VersionMatcher)
     final LazyDependencyToModuleResolver resolver = new LazyDependencyToModuleResolver(target, matcher)
 
     def "does not resolve module for static version dependency until requested"() {
diff --git a/subprojects/core-impl/src/test/groovy/org/gradle/api/internal/artifacts/ivyservice/ivyresolve/UserResolverChainTest.groovy b/subprojects/core-impl/src/test/groovy/org/gradle/api/internal/artifacts/ivyservice/ivyresolve/UserResolverChainTest.groovy
index 2afc920..ba14783 100644
--- a/subprojects/core-impl/src/test/groovy/org/gradle/api/internal/artifacts/ivyservice/ivyresolve/UserResolverChainTest.groovy
+++ b/subprojects/core-impl/src/test/groovy/org/gradle/api/internal/artifacts/ivyservice/ivyresolve/UserResolverChainTest.groovy
@@ -19,42 +19,38 @@ package org.gradle.api.internal.artifacts.ivyservice.ivyresolve
 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.plugins.latest.LatestRevisionStrategy
-import org.apache.ivy.plugins.resolver.ResolverSettings
-import org.apache.ivy.plugins.version.VersionMatcher
 import org.gradle.api.artifacts.ModuleVersionIdentifier
 import org.gradle.api.artifacts.ModuleVersionSelector
 import org.gradle.api.internal.artifacts.DefaultModuleVersionIdentifier
 import org.gradle.api.internal.artifacts.ivyservice.BuildableModuleVersionResolveResult
+import org.gradle.api.internal.artifacts.ivyservice.ivyresolve.strategy.LatestStrategy
+import org.gradle.api.internal.artifacts.ivyservice.ivyresolve.strategy.VersionMatcher
 import spock.lang.Specification
 
 class UserResolverChainTest extends Specification {
-    final UserResolverChain resolver = new UserResolverChain()
-    final ModuleVersionSelector dependencyId = Stub()
-    final DependencyMetaData dependency = Stub()
-    final DependencyDescriptor dependencyDescriptor = Stub()
     final ModuleDescriptor descriptor = descriptor("1.2")
     final ModuleVersionIdentifier resolvedId = moduleVersionIdentifier(descriptor)
+    final dependencyId = Stub(ModuleVersionSelector)
+    final dependency = Stub(DependencyMetaData)
+    final dependencyDescriptor = Stub(DependencyDescriptor)
+    final matcher = Stub(VersionMatcher)
+    final latestStrategy = Stub(LatestStrategy)
+    final result = Mock(BuildableModuleVersionResolveResult)
+    final moduleSource = Mock(ModuleSource)
+
+    final UserResolverChain resolver = new UserResolverChain(matcher, latestStrategy)
 
     ModuleVersionIdentifier moduleVersionIdentifier(ModuleDescriptor moduleDescriptor) {
         def moduleRevId = moduleDescriptor.moduleRevisionId
         new DefaultModuleVersionIdentifier(moduleRevId.organisation, moduleRevId.name, moduleRevId.revision)
     }
 
-    final BuildableModuleVersionResolveResult result = Mock()
-    final VersionMatcher matcher = Stub()
-    final ModuleSource moduleSource = Mock()
-
     def setup() {
         _ * dependencyId.group >> "group"
         _ * dependencyId.name >> "project"
         _ * dependencyId.version >> "1.0"
         _ * dependency.requested >> dependencyId
         _ * dependency.descriptor >> dependencyDescriptor
-        def settings = Stub(ResolverSettings)
-        _ * settings.versionMatcher >> matcher
-        _ * settings.defaultLatestStrategy >> new LatestRevisionStrategy();
-        resolver.settings = settings
     }
 
     def "uses local dependency when available"() {
diff --git a/subprojects/core-impl/src/test/groovy/org/gradle/api/internal/artifacts/ivyservice/ivyresolve/memcache/CachedModuleVersionResultTest.groovy b/subprojects/core-impl/src/test/groovy/org/gradle/api/internal/artifacts/ivyservice/ivyresolve/memcache/CachedModuleVersionResultTest.groovy
new file mode 100644
index 0000000..29a2f20
--- /dev/null
+++ b/subprojects/core-impl/src/test/groovy/org/gradle/api/internal/artifacts/ivyservice/ivyresolve/memcache/CachedModuleVersionResultTest.groovy
@@ -0,0 +1,85 @@
+/*
+ * Copyright 2013 the original author or authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY 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.ivyresolve.memcache
+
+import org.gradle.api.internal.artifacts.ivyservice.ivyresolve.BuildableModuleVersionMetaDataResolveResult
+import org.gradle.api.internal.artifacts.ivyservice.ivyresolve.ModuleVersionMetaData
+import spock.lang.Specification
+
+class CachedModuleVersionResultTest extends Specification {
+
+    def "knows if result is cachable"() {
+        def resolved = Mock(BuildableModuleVersionMetaDataResolveResult) {
+            getState() >> BuildableModuleVersionMetaDataResolveResult.State.Resolved
+            getMetaData() >> Stub(ModuleVersionMetaData)
+        }
+        def missing = Mock(BuildableModuleVersionMetaDataResolveResult) { getState() >> BuildableModuleVersionMetaDataResolveResult.State.Missing }
+        def probablyMissing = Mock(BuildableModuleVersionMetaDataResolveResult) { getState() >> BuildableModuleVersionMetaDataResolveResult.State.ProbablyMissing }
+        def failed = Mock(BuildableModuleVersionMetaDataResolveResult) { getState() >> BuildableModuleVersionMetaDataResolveResult.State.Failed }
+
+        expect:
+        new CachedModuleVersionResult(resolved).cacheable
+        new CachedModuleVersionResult(missing).cacheable
+        new CachedModuleVersionResult(probablyMissing).cacheable
+        !new CachedModuleVersionResult(failed).cacheable
+    }
+
+    def "interrogates result only when resolved"() {
+        def resolved = Mock(BuildableModuleVersionMetaDataResolveResult)
+        def missing = Mock(BuildableModuleVersionMetaDataResolveResult)
+
+        when:
+        new CachedModuleVersionResult(missing)
+
+        then:
+        1 * missing.getState() >> BuildableModuleVersionMetaDataResolveResult.State.Missing
+        0 * missing._
+
+        when:
+        new CachedModuleVersionResult(resolved)
+
+        then:
+        1 * resolved.getState() >> BuildableModuleVersionMetaDataResolveResult.State.Resolved
+        1 * resolved.getMetaData() >> Stub(ModuleVersionMetaData)
+    }
+
+    def "supplies cached data"() {
+        def resolved = Mock(BuildableModuleVersionMetaDataResolveResult) {
+            getState() >> BuildableModuleVersionMetaDataResolveResult.State.Resolved
+            getMetaData() >> Stub(ModuleVersionMetaData)
+        }
+        def missing = Mock(BuildableModuleVersionMetaDataResolveResult) { getState() >> BuildableModuleVersionMetaDataResolveResult.State.Missing }
+        def probablyMissing = Mock(BuildableModuleVersionMetaDataResolveResult) { getState() >> BuildableModuleVersionMetaDataResolveResult.State.ProbablyMissing }
+
+        def result = Mock(BuildableModuleVersionMetaDataResolveResult)
+
+        when:
+        new CachedModuleVersionResult(resolved).supply(result)
+        then:
+        1 * result.resolved(_, _, _, _)
+
+        when:
+        new CachedModuleVersionResult(missing).supply(result)
+        then:
+        1 * result.missing()
+
+        when:
+        new CachedModuleVersionResult(probablyMissing).supply(result)
+        then:
+        1 * result.probablyMissing()
+    }
+}
diff --git a/subprojects/core-impl/src/test/groovy/org/gradle/api/internal/artifacts/ivyservice/ivyresolve/memcache/CachedRepositoryTest.groovy b/subprojects/core-impl/src/test/groovy/org/gradle/api/internal/artifacts/ivyservice/ivyresolve/memcache/CachedRepositoryTest.groovy
new file mode 100644
index 0000000..9e9a1a5
--- /dev/null
+++ b/subprojects/core-impl/src/test/groovy/org/gradle/api/internal/artifacts/ivyservice/ivyresolve/memcache/CachedRepositoryTest.groovy
@@ -0,0 +1,117 @@
+/*
+ * Copyright 2013 the original author or authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY 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.ivyresolve.memcache
+
+import org.gradle.api.artifacts.ArtifactIdentifier
+import org.gradle.api.internal.artifacts.ivyservice.BuildableArtifactResolveResult
+import org.gradle.api.internal.artifacts.ivyservice.ivyresolve.BuildableModuleVersionMetaDataResolveResult
+import org.gradle.api.internal.artifacts.ivyservice.ivyresolve.DependencyMetaData
+import org.gradle.api.internal.artifacts.ivyservice.ivyresolve.LocalAwareModuleVersionRepository
+import org.gradle.api.internal.artifacts.ivyservice.ivyresolve.ModuleSource
+import spock.lang.Specification
+
+import static org.gradle.api.internal.artifacts.DefaultModuleVersionSelector.newSelector
+
+class CachedRepositoryTest extends Specification {
+
+    def stats = new DependencyMetadataCacheStats()
+    def cache = Mock(DependencyMetadataCache)
+    def delegate = Mock(LocalAwareModuleVersionRepository)
+    def repo = new CachedRepository(cache, delegate, stats)
+
+    def lib = newSelector("org", "lib", "1.0")
+    def dep = Stub(DependencyMetaData) { getRequested() >> lib }
+    def result = Mock(BuildableModuleVersionMetaDataResolveResult)
+
+    def "delegates"() {
+        when:
+        def id = repo.getId()
+        def name = repo.getName()
+
+        then:
+        id == "x"
+        name == "localRepo"
+        1 * delegate.getId() >> "x"
+        1 * delegate.getName() >> "localRepo"
+    }
+
+    def "retrieves and caches local dependencies"() {
+        when:
+        repo.getLocalDependency(dep, result)
+
+        then:
+        1 * cache.supplyLocalMetaData(lib, result) >> false
+        1 * delegate.getLocalDependency(dep, result)
+        1 * cache.newLocalDependencyResult(lib, result)
+        0 * _
+    }
+
+    def "uses local dependencies from cache"() {
+        when:
+        repo.getLocalDependency(dep, result)
+
+        then:
+        1 * cache.supplyLocalMetaData(lib, result) >> true
+        0 * _
+    }
+
+    def "retrieves and caches dependencies"() {
+        when:
+        repo.getDependency(dep, result)
+
+        then:
+        1 * cache.supplyMetaData(lib, result) >> false
+        1 * delegate.getDependency(dep, result)
+        1 * cache.newDependencyResult(lib, result)
+        0 * _
+    }
+
+    def "uses dependencies from cache"() {
+        when:
+        repo.getDependency(dep, result)
+
+        then:
+        1 * cache.supplyMetaData(lib, result) >> true
+        0 * _
+    }
+
+    def "retrieves and caches artifacts"() {
+        def result = Mock(BuildableArtifactResolveResult)
+        def artifact = Stub(ArtifactIdentifier)
+        def source = Mock(ModuleSource)
+
+        when:
+        repo.resolve(artifact, result, source)
+
+        then:
+        1 * cache.supplyArtifact(artifact, result) >> false
+        1 * delegate.resolve(artifact, result, source)
+        1 * cache.newArtifact(artifact, result)
+        0 * _
+    }
+
+    def "uses artifacts from cache"() {
+        def result = Mock(BuildableArtifactResolveResult)
+        def artifact = Stub(ArtifactIdentifier)
+
+        when:
+        repo.resolve(artifact, result, Mock(ModuleSource))
+
+        then:
+        1 * cache.supplyArtifact(artifact, result) >> true
+        0 * _
+    }
+}
diff --git a/subprojects/core-impl/src/test/groovy/org/gradle/api/internal/artifacts/ivyservice/ivyresolve/memcache/DependencyMetadataCacheTest.groovy b/subprojects/core-impl/src/test/groovy/org/gradle/api/internal/artifacts/ivyservice/ivyresolve/memcache/DependencyMetadataCacheTest.groovy
new file mode 100644
index 0000000..cd4ff8f
--- /dev/null
+++ b/subprojects/core-impl/src/test/groovy/org/gradle/api/internal/artifacts/ivyservice/ivyresolve/memcache/DependencyMetadataCacheTest.groovy
@@ -0,0 +1,126 @@
+/*
+ * Copyright 2013 the original author or authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY 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.ivyresolve.memcache
+
+import org.gradle.api.artifacts.ArtifactIdentifier
+import org.gradle.api.internal.artifacts.ivyservice.BuildableArtifactResolveResult
+import org.gradle.api.internal.artifacts.ivyservice.ivyresolve.BuildableModuleVersionMetaDataResolveResult
+import org.gradle.api.internal.artifacts.ivyservice.ivyresolve.ModuleVersionMetaData
+import spock.lang.Specification
+
+import static org.gradle.api.internal.artifacts.DefaultModuleVersionIdentifier.newId
+import static org.gradle.api.internal.artifacts.DefaultModuleVersionSelector.newSelector
+
+class DependencyMetadataCacheTest extends Specification {
+
+    def stats = new DependencyMetadataCacheStats()
+    def cache = new DependencyMetadataCache(stats)
+
+    def "caches and supplies remote metadata"() {
+        def resolvedResult = Mock(BuildableModuleVersionMetaDataResolveResult.class) {
+            getState() >> BuildableModuleVersionMetaDataResolveResult.State.Resolved
+            getMetaData() >> Stub(ModuleVersionMetaData)
+        }
+        cache.newDependencyResult(newSelector("org", "foo", "1.0"), resolvedResult)
+        def result = Mock(BuildableModuleVersionMetaDataResolveResult.class)
+
+        when:
+        def local = cache.supplyLocalMetaData(newSelector("org", "foo", "1.0"), result)
+        def differentSelector = cache.supplyMetaData(newSelector("org", "XXX", "1.0"), result)
+
+        then:
+        !local
+        !differentSelector
+        stats.metadataServed == 0
+        0 * result._
+
+        when:
+        def match = cache.supplyMetaData(newSelector("org", "foo", "1.0"), result)
+
+        then:
+        match
+        stats.metadataServed == 1
+        1 * result.resolved(_, _, _, _)
+    }
+
+    def "caches and supplies remote and local metadata"() {
+        def resolvedLocal = Mock(BuildableModuleVersionMetaDataResolveResult.class) {
+            getMetaData() >> Mock(ModuleVersionMetaData)
+            getState() >> BuildableModuleVersionMetaDataResolveResult.State.Resolved
+        }
+        def resolvedRemote = Mock(BuildableModuleVersionMetaDataResolveResult.class) {
+            getMetaData() >> Mock(ModuleVersionMetaData)
+            getState() >> BuildableModuleVersionMetaDataResolveResult.State.Resolved
+        }
+
+        cache.newDependencyResult(newSelector("org", "remote", "1.0"), resolvedRemote)
+        cache.newLocalDependencyResult(newSelector("org", "local", "1.0"), resolvedLocal)
+
+        def result = Mock(BuildableModuleVersionMetaDataResolveResult.class)
+
+        when:
+        def local = cache.supplyLocalMetaData(newSelector("org", "local", "1.0"), result)
+        def remote = cache.supplyMetaData(newSelector("org", "remote", "1.0"), result)
+
+        then:
+        local
+        remote
+        stats.metadataServed == 2
+        1 * result.resolved(_, _, _, _)
+        1 * result.resolved(_, _, _, _)
+    }
+
+    def "does not cache failed resolves"() {
+        def failedResult = Mock(BuildableModuleVersionMetaDataResolveResult.class) { getState() >> BuildableModuleVersionMetaDataResolveResult.State.Failed }
+        cache.newDependencyResult(newSelector("org", "lib", "1.0"), failedResult)
+
+        def result = Mock(BuildableModuleVersionMetaDataResolveResult.class)
+
+        when:
+        def fromCache = cache.supplyMetaData(newSelector("org", "lib", "1.0"), result)
+
+        then:
+        !fromCache
+        0 * result._
+    }
+
+    def "caches and supplies artifacts"() {
+        def foo = Stub(ArtifactIdentifier) { getModuleVersionIdentifier() >> newId("org", "foo", "1.0") }
+        def fooFile = new File("foo")
+        def fooResult = Mock(BuildableArtifactResolveResult) { getFile() >> fooFile }
+        def anotherFooResult = Mock(BuildableArtifactResolveResult)
+
+        def different = Stub(ArtifactIdentifier) { getModuleVersionIdentifier() >> newId("org", "XXX", "1.0") }
+        def differentResult = Mock(BuildableArtifactResolveResult)
+
+        cache.newArtifact(foo, fooResult)
+
+        when:
+        def differentCached = cache.supplyArtifact(different, differentResult )
+
+        then:
+        !differentCached
+        0 * differentResult._
+
+        when:
+        def fooCached = cache.supplyArtifact(foo, anotherFooResult )
+
+        then:
+        fooCached
+        1 * anotherFooResult.resolved(fooFile)
+    }
+}
diff --git a/subprojects/core-impl/src/test/groovy/org/gradle/api/internal/artifacts/ivyservice/ivyresolve/memcache/InMemoryDependencyMetadataCacheTest.groovy b/subprojects/core-impl/src/test/groovy/org/gradle/api/internal/artifacts/ivyservice/ivyresolve/memcache/InMemoryDependencyMetadataCacheTest.groovy
new file mode 100644
index 0000000..fe3067b
--- /dev/null
+++ b/subprojects/core-impl/src/test/groovy/org/gradle/api/internal/artifacts/ivyservice/ivyresolve/memcache/InMemoryDependencyMetadataCacheTest.groovy
@@ -0,0 +1,76 @@
+/*
+ * Copyright 2013 the original author or authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY 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.ivyresolve.memcache
+
+import org.gradle.api.internal.artifacts.ivyservice.ivyresolve.LocalAwareModuleVersionRepository
+import org.gradle.util.SetSystemProperties
+import org.junit.Rule
+import spock.lang.Specification
+
+class InMemoryDependencyMetadataCacheTest extends Specification {
+
+    @Rule SetSystemProperties sysProp = new SetSystemProperties()
+    def cache = new InMemoryDependencyMetadataCache()
+
+    def "can be turned off via system property"() {
+        System.properties.setProperty(InMemoryDependencyMetadataCache.TOGGLE_PROPERTY, "false")
+        def repo = Mock(LocalAwareModuleVersionRepository) { getId() >> "mavenCentral" }
+
+        when:
+        def out = cache.cached(repo)
+
+        then:
+        out.is(repo)
+    }
+
+    def "wraps repositories"() {
+        def repo1 = Mock(LocalAwareModuleVersionRepository) { getId() >> "mavenCentral" }
+        def repo2 = Mock(LocalAwareModuleVersionRepository) { getId() >> "localRepo" }
+        def repo3 = Mock(LocalAwareModuleVersionRepository) { getId() >> "mavenCentral" }
+
+        when:
+        CachedRepository c1 = cache.cached(repo1)
+        CachedRepository c2 = cache.cached(repo2)
+        CachedRepository c3 = cache.cached(repo3)
+
+        then:
+        c1.delegate == repo1
+        c2.delegate == repo2
+        c3.delegate == repo3
+
+        c1.cache == c3.cache //same repo id, same cache
+        c2.cache != c1.cache
+
+        cache.stats.reposWrapped == 3
+        cache.stats.cacheInstances == 2
+
+        cache.cachePerRepo.size() == 2
+    }
+
+    def "cleans cache on close"() {
+        when:
+        cache.cached(Mock(LocalAwareModuleVersionRepository) { getId() >> "x"} )
+        cache.stop()
+
+        then:
+        cache.cachePerRepo.isEmpty()
+    }
+}
diff --git a/subprojects/core-impl/src/test/groovy/org/gradle/api/internal/artifacts/ivyservice/ivyresolve/parser/DownloadedIvyModuleDescriptorParserTest.groovy b/subprojects/core-impl/src/test/groovy/org/gradle/api/internal/artifacts/ivyservice/ivyresolve/parser/DownloadedIvyModuleDescriptorParserTest.groovy
index a7162e4..39e458d 100644
--- a/subprojects/core-impl/src/test/groovy/org/gradle/api/internal/artifacts/ivyservice/ivyresolve/parser/DownloadedIvyModuleDescriptorParserTest.groovy
+++ b/subprojects/core-impl/src/test/groovy/org/gradle/api/internal/artifacts/ivyservice/ivyresolve/parser/DownloadedIvyModuleDescriptorParserTest.groovy
@@ -16,9 +16,6 @@
 
 package org.gradle.api.internal.artifacts.ivyservice.ivyresolve.parser
 
-import org.apache.ivy.core.settings.IvySettings
-import org.apache.ivy.plugins.parser.ParserSettings
-import org.apache.ivy.plugins.repository.Resource
 import org.gradle.test.fixtures.file.TestNameTestDirectoryProvider
 import org.junit.Rule
 import spock.lang.Specification
@@ -26,6 +23,7 @@ import spock.lang.Specification
 class DownloadedIvyModuleDescriptorParserTest extends Specification {
     @Rule TestNameTestDirectoryProvider tmpDir
     final DownloadedIvyModuleDescriptorParser parser = new DownloadedIvyModuleDescriptorParser()
+    final parserSettings = Mock(DescriptorParseContext)
 
     def "discards the default attribute"() {
         def ivyFile = tmpDir.createFile("ivy.xml")
@@ -34,12 +32,9 @@ class DownloadedIvyModuleDescriptorParserTest extends Specification {
     <info organisation="org" module="someModule" revision="1.2" default="true"/>
 </ivy-module>
 """
-        def url = ivyFile.toURI().toURL()
-        ParserSettings settings = new IvySettings()
-        Resource resource = Mock()
-
         when:
-        def descriptor = parser.parseDescriptor(settings, url, resource, true)
+        parserSettings.substitute(_ as String) >> {String value -> value}
+        def descriptor = parser.parseDescriptor(parserSettings, ivyFile, true)
 
         then:
         !descriptor.default
diff --git a/subprojects/core-impl/src/test/groovy/org/gradle/api/internal/artifacts/ivyservice/ivyresolve/parser/GradlePomModuleDescriptorParserTest.groovy b/subprojects/core-impl/src/test/groovy/org/gradle/api/internal/artifacts/ivyservice/ivyresolve/parser/GradlePomModuleDescriptorParserTest.groovy
index 57a5d3a..57be866 100644
--- a/subprojects/core-impl/src/test/groovy/org/gradle/api/internal/artifacts/ivyservice/ivyresolve/parser/GradlePomModuleDescriptorParserTest.groovy
+++ b/subprojects/core-impl/src/test/groovy/org/gradle/api/internal/artifacts/ivyservice/ivyresolve/parser/GradlePomModuleDescriptorParserTest.groovy
@@ -14,12 +14,11 @@
  * limitations under the License.
  */
 package org.gradle.api.internal.artifacts.ivyservice.ivyresolve.parser
-
 import org.apache.ivy.core.module.descriptor.DependencyDescriptor
 import org.apache.ivy.core.module.descriptor.ModuleDescriptor
 import org.apache.ivy.core.module.id.ArtifactRevisionId
+import org.apache.ivy.core.module.id.ModuleId
 import org.apache.ivy.core.module.id.ModuleRevisionId
-import org.apache.ivy.plugins.parser.ParserSettings
 import org.gradle.test.fixtures.file.TestFile
 import org.gradle.test.fixtures.file.TestNameTestDirectoryProvider
 import org.junit.Rule
@@ -29,7 +28,7 @@ import spock.lang.Specification
 class GradlePomModuleDescriptorParserTest extends Specification {
     @Rule public final TestNameTestDirectoryProvider tmpDir = new TestNameTestDirectoryProvider()
     final GradlePomModuleDescriptorParser parser = new GradlePomModuleDescriptorParser()
-    final ModuleScopedParserSettings ivySettings = Mock()
+    final parseContext = Mock(DescriptorParseContext)
     TestFile pomFile
 
     def "setup"() {
@@ -57,7 +56,7 @@ class GradlePomModuleDescriptorParserTest extends Specification {
 </project>
 """
         and:
-        ivySettings.currentRevisionId >> moduleId('group-one', 'artifact-one', 'version-one')
+        parseContext.currentRevisionId >> moduleId('group-one', 'artifact-one', 'version-one')
 
         when:
         def descriptor = parsePom()
@@ -70,6 +69,55 @@ class GradlePomModuleDescriptorParserTest extends Specification {
         hasDefaultDependencyArtifact(descriptor.dependencies.first())
     }
 
+    def "uses dependency management section to provide default values for a dependency"() {
+        given:
+        pomFile << """
+<project>
+    <modelVersion>4.0.0</modelVersion>
+    <groupId>group-one</groupId>
+    <artifactId>artifact-one</artifactId>
+    <version>version-one</version>
+
+    <dependencies>
+        <dependency>
+            <groupId>group-two</groupId>
+            <artifactId>artifact-two</artifactId>
+        </dependency>
+    </dependencies>
+    <dependencyManagement>
+        <dependencies>
+            <dependency>
+                <groupId>group-two</groupId>
+                <artifactId>artifact-two</artifactId>
+                <version>1.2</version>
+                <scope>test</scope>
+                <exclusions>
+                    <exclusion>
+                        <groupId>group-three</groupId>
+                        <artifactId>artifact-three</artifactId>
+                    </exclusion>
+                </exclusions>
+            </dependency>
+        </dependencies>
+    </dependencyManagement>
+</project>
+"""
+        and:
+        parseContext.currentRevisionId >> moduleId('group-one', 'artifact-one', 'version-one')
+
+        when:
+        def descriptor = parsePom()
+
+        then:
+        descriptor.dependencies.length == 1
+        def dep = descriptor.dependencies.first()
+        dep.dependencyRevisionId == moduleId('group-two', 'artifact-two', '1.2')
+        dep.moduleConfigurations == ['test']
+        dep.allExcludeRules.length == 1
+        dep.allExcludeRules.first().id.moduleId == ModuleId.newInstance('group-three', 'artifact-three')
+        hasDefaultDependencyArtifact(dep)
+    }
+
     def "pom with dependency with classifier"() {
         given:
         pomFile << """
@@ -90,7 +138,7 @@ class GradlePomModuleDescriptorParserTest extends Specification {
 </project>
 """
         and:
-        ivySettings.currentRevisionId >> moduleId('group-one', 'artifact-one', 'version-one')
+        parseContext.currentRevisionId >> moduleId('group-one', 'artifact-one', 'version-one')
 
         when:
         def descriptor = parsePom()
@@ -124,7 +172,7 @@ class GradlePomModuleDescriptorParserTest extends Specification {
 </project>
 """
         and:
-        ivySettings.currentRevisionId >> moduleId('group-one', 'artifact-one', 'version-one')
+        parseContext.currentRevisionId >> moduleId('group-one', 'artifact-one', 'version-one')
 
         when:
         def descriptor = parsePom()
@@ -150,7 +198,7 @@ class GradlePomModuleDescriptorParserTest extends Specification {
 </project>
 """
         and:
-        ivySettings.currentRevisionId >> moduleId('group-one', 'artifact-one', 'version-one')
+        parseContext.currentRevisionId >> moduleId('group-one', 'artifact-one', 'version-one')
 
         when:
         def descriptor = parsePom()
@@ -161,8 +209,25 @@ class GradlePomModuleDescriptorParserTest extends Specification {
         descriptor.dependencies.length == 0
     }
 
+    def "fails when POM is not well formed XML"() {
+        given:
+        pomFile << """
+<project>
+    <modelVersion
+</project>
+"""
+
+        when:
+        parsePom()
+
+        then:
+        def e = thrown(MetaDataParseException)
+        e.message == "Could not parse POM ${pomFile.toURI()}"
+        e.cause.message.contains('Element type "modelVersion"')
+    }
+
     private ModuleDescriptor parsePom() {
-        parser.parseDescriptor(ivySettings, pomFile.toURI().toURL(), false)
+        parser.parseDescriptor(parseContext, pomFile, true)
     }
 
     private void hasArtifact(ModuleDescriptor descriptor, String name, String type, String ext, String classifier = null) {
diff --git a/subprojects/core-impl/src/test/groovy/org/gradle/api/internal/artifacts/ivyservice/ivyresolve/parser/IvyXmlModuleDescriptorParserTest.groovy b/subprojects/core-impl/src/test/groovy/org/gradle/api/internal/artifacts/ivyservice/ivyresolve/parser/IvyXmlModuleDescriptorParserTest.groovy
index b8f0ba2..f442c40 100644
--- a/subprojects/core-impl/src/test/groovy/org/gradle/api/internal/artifacts/ivyservice/ivyresolve/parser/IvyXmlModuleDescriptorParserTest.groovy
+++ b/subprojects/core-impl/src/test/groovy/org/gradle/api/internal/artifacts/ivyservice/ivyresolve/parser/IvyXmlModuleDescriptorParserTest.groovy
@@ -16,35 +16,51 @@
 
 package org.gradle.api.internal.artifacts.ivyservice.ivyresolve.parser
 import org.apache.ivy.core.module.descriptor.*
-import org.apache.ivy.core.settings.IvySettings
 import org.apache.ivy.plugins.matcher.ExactPatternMatcher
 import org.apache.ivy.plugins.matcher.GlobPatternMatcher
 import org.apache.ivy.plugins.matcher.PatternMatcher
+import org.apache.ivy.plugins.matcher.RegexpPatternMatcher
 import org.gradle.test.fixtures.file.TestNameTestDirectoryProvider
 import org.gradle.util.Resources
-import org.hamcrest.Matchers
 import org.junit.Rule
+import spock.lang.Issue
 import spock.lang.Specification
 
-import java.text.ParseException
-
-import static junit.framework.Assert.*
-import static org.junit.Assert.assertThat
+import static org.junit.Assert.*
 
 class IvyXmlModuleDescriptorParserTest extends Specification {
-    @Rule public final Resources resources = new Resources()
+    @Rule
+    public final Resources resources = new Resources()
     @Rule TestNameTestDirectoryProvider temporaryFolder = new TestNameTestDirectoryProvider()
 
     IvyXmlModuleDescriptorParser parser = new IvyXmlModuleDescriptorParser()
-    IvySettings settings = new IvySettings()
+    DescriptorParseContext parseContext = Mock()
 
     def setup() {
-        settings.setDefaultCache(temporaryFolder.createDir("ivy/cache"))
+        parseContext.substitute(_ as String) >> {String value -> value}
+        parseContext.getMatcher("exact") >> ExactPatternMatcher.INSTANCE
+        parseContext.getMatcher("glob") >> GlobPatternMatcher.INSTANCE
+        parseContext.getMatcher("regexp") >> RegexpPatternMatcher.INSTANCE
     }
 
-    def testEmptyDependencies() throws Exception {
+    def "parses Ivy descriptor with empty dependencies section"() throws Exception {
         when:
-        ModuleDescriptor md = parser.parseDescriptor(settings, resources.getResource("test-empty-dependencies.xml").toURI().toURL(), true)
+        def file = temporaryFolder.file("ivy.xml") << """
+<ivy-module version="1.0">
+    <info organisation="myorg"
+          module="mymodule"
+          revision="myrev"
+          status="integration"
+          publication="20041101110000"
+    />
+    <dependencies>
+    </dependencies>
+</ivy-module>
+"""
+        ModuleDescriptor md = parser.parseDescriptor(parseContext, file, true)
+
+        and:
+
         then:
         md != null
         "myorg" == md.getModuleRevisionId().getOrganisation()
@@ -61,25 +77,90 @@ class IvyXmlModuleDescriptorParserTest extends Specification {
         0 == md.getDependencies().length
     }
 
-    public void testBadConfs() throws IOException {
+    public void "fails when configuration extends an unknown configuration"() throws IOException {
+        def file = temporaryFolder.file("ivy.xml") << """
+<ivy-module version="1.0">
+    <info organisation="myorg"
+          module="mymodule"
+          status="integration"
+    />
+    <configurations>
+        <conf name="A" extends="invalidConf"/>
+    </configurations>
+</ivy-module>
+"""
+
+        when:
+        parser.parseDescriptor(parseContext, file, true)
+
+        then:
+        def e = thrown(MetaDataParseException)
+        e.message == "Could not parse Ivy file ${file.toURI()}"
+        e.cause.message == "unknown configuration 'invalidConf'. It is extended by A"
+    }
+
+    public void "fails when there is a cycle in configuration hierarchy"() throws IOException {
+        def file = temporaryFolder.file("ivy.xml") << """
+<ivy-module version="1.0">
+    <info organisation="myorg"
+          module="mymodule"
+          status="integration"
+    />
+    <configurations>
+        <conf name="A" extends="B"/>
+        <conf name="B" extends="A"/>
+    </configurations>
+</ivy-module>
+"""
+
+        when:
+        parser.parseDescriptor(parseContext, file, true)
+
+        then:
+        def e = thrown(MetaDataParseException)
+        e.message == "Could not parse Ivy file ${file.toURI()}"
+        e.cause.message == "illegal cycle detected in configuration extension: A => B => A"
+    }
+
+    public void "fails when descriptor contains badly formed XML"() {
+        def file = temporaryFolder.file("ivy.xml") << """
+<ivy-module version="1.0">
+    <info
+</ivy-module>
+"""
+
         when:
-        parser.parseDescriptor(settings, resources.getResource("test-bad-confs.xml").toURI().toURL(), true)
+        parser.parseDescriptor(parseContext, file, true)
+
         then:
-        def e = thrown(ParseException)
-        assertThat(e.message, Matchers.startsWith("unknown configuration 'invalidConf'"))
+        def e = thrown(MetaDataParseException)
+        e.message == "Could not parse Ivy file ${file.toURI()}"
+        e.cause.message.contains('Element type "info"')
     }
 
-    public void testCyclicConfs() throws IOException {
+    public void "fails when descriptor does not match schema"() {
+        def file = temporaryFolder.file("ivy.xml") << """
+<ivy-module version="1.0">
+    <not-an-ivy-file/>
+</ivy-module>
+"""
+
         when:
-        parser.parseDescriptor(settings, resources.getResource("test-cyclic-confs1.xml").toURI().toURL(), true)
+        parser.parseDescriptor(parseContext, file, true)
+
         then:
-        def e = thrown(ParseException)
-        assertThat(e.message, Matchers.startsWith("illegal cycle detected in configuration extension: A => B => A"))
+        def e = thrown(MetaDataParseException)
+        e.message == "Could not parse Ivy file ${file.toURI()}"
+        e.cause.message.contains('unknown tag not-an-ivy-file')
     }
 
-    public void testFull() throws Exception {
+    public void "parses a full Ivy descriptor"() throws Exception {
+        def file = temporaryFolder.file("ivy.xml")
+        file.text = resources.getResource("test-full.xml").text
+
         when:
-        ModuleDescriptor md = parser.parseDescriptor(settings, resources.getResource("test-full.xml").toURI().toURL(), false)
+        ModuleDescriptor md = parser.parseDescriptor(parseContext, file, true)
+
         then:
         assertNotNull(md)
         assertEquals("myorg", md.getModuleRevisionId().getOrganisation())
@@ -136,6 +217,128 @@ class IvyXmlModuleDescriptorParserTest extends Specification {
         true
     }
 
+    @Issue("http://issues.gradle.org/browse/GRADLE-2766")
+    def "defaultconfmapping is respected"() {
+        given:
+        def file = temporaryFolder.createFile("ivy.xml")
+        file.text = """
+           <ivy-module version="2.0" xmlns:e="http://ant.apache.org/ivy/extra">
+                <info organisation="myorg"
+                      module="mymodule"
+                      revision="myrev"
+                      status="integration"
+                      publication="20041101110000">
+                </info>
+                <configurations>
+                    <conf name="myconf" />
+                </configurations>
+                <publications/>
+                <dependencies defaultconfmapping="myconf->default">
+                    <dependency name="mymodule2"/>
+                </dependencies>
+            </ivy-module>
+        """
+
+        when:
+        def descriptor = parser.parseDescriptor(parseContext, file, false)
+        def dependency = descriptor.dependencies.first()
+
+        then:
+        dependency.moduleConfigurations == ["myconf"]
+    }
+
+    def "parses dependency config mappings"() {
+        given:
+        def file = temporaryFolder.createFile("ivy.xml")
+        file.text = """
+           <ivy-module version="2.0" xmlns:e="http://ant.apache.org/ivy/extra">
+                <info organisation="myorg"
+                      module="mymodule"
+                      revision="myrev">
+                </info>
+                <configurations>
+                    <conf name="a" />
+                    <conf name="b" />
+                    <conf name="c" />
+                </configurations>
+                <publications/>
+                <dependencies>
+                    <dependency name="mymodule2" conf="a"/>
+                    <dependency name="mymodule2" conf="a->other"/>
+                    <dependency name="mymodule2" conf="*->@"/>
+                    <dependency name="mymodule2" conf="a->other;%->@"/>
+                    <dependency name="mymodule2" conf="*,!a->@"/>
+                    <dependency name="mymodule2" conf="a->*"/>
+                    <dependency name="mymodule2" conf="a->one,two;a,b->three;*->four;%->none"/>
+                    <dependency name="mymodule2" conf="a->#"/>
+                    <dependency name="mymodule2" conf="a->a;%->@"/>
+                    <dependency name="mymodule2" conf="a->a;*,!a->b"/>
+                </dependencies>
+            </ivy-module>
+        """
+
+        when:
+        def descriptor = parser.parseDescriptor(parseContext, file, false)
+
+        then:
+        def dependency1 = descriptor.dependencies[0]
+        dependency1.moduleConfigurations == ["a"]
+        dependency1.getDependencyConfigurations("a") == ["a"]
+        dependency1.getDependencyConfigurations("a", "requested") == ["a"]
+
+        def dependency2 = descriptor.dependencies[1]
+        dependency2.moduleConfigurations == ["a"]
+        dependency2.getDependencyConfigurations("a") == ["other"]
+        dependency2.getDependencyConfigurations("a", "requested") == ["other"]
+
+        def dependency3 = descriptor.dependencies[2]
+        dependency3.moduleConfigurations == ["*"]
+        dependency3.getDependencyConfigurations("a") == ["a"]
+        dependency3.getDependencyConfigurations("a", "requested") == ["a"]
+
+        def dependency4 = descriptor.dependencies[3]
+        dependency4.moduleConfigurations == ["a", "%"]
+        dependency4.getDependencyConfigurations("a") == ["other"]
+        dependency4.getDependencyConfigurations("a", "requested") == ["other"]
+        dependency4.getDependencyConfigurations("b") == ["b"]
+        dependency4.getDependencyConfigurations("b", "requested") == ["b"]
+
+        def dependency5 = descriptor.dependencies[4]
+        dependency5.moduleConfigurations == ["*", "!a"]
+        dependency5.getDependencyConfigurations("a") == ["a"]
+        dependency5.getDependencyConfigurations("a", "requested") == ["a"]
+
+        def dependency6 = descriptor.dependencies[5]
+        dependency6.moduleConfigurations == ["a"]
+        dependency6.getDependencyConfigurations("a") == ["*"]
+        dependency6.getDependencyConfigurations("a", "requested") == ["*"]
+
+        def dependency7 = descriptor.dependencies[6]
+        dependency7.moduleConfigurations == ["a", "b", "*", "%"]
+        dependency7.getDependencyConfigurations("a") == ["one", "two", "three", "four"]
+        dependency7.getDependencyConfigurations("b") == ["three", "four"]
+        dependency7.getDependencyConfigurations("c") == ["none", "four"]
+
+        def dependency8 = descriptor.dependencies[7]
+        dependency8.moduleConfigurations == ["a"]
+        dependency8.getDependencyConfigurations("a") == ["a"]
+        dependency8.getDependencyConfigurations("a", "requested") == ["requested"]
+
+        def dependency9 = descriptor.dependencies[8]
+        dependency9.moduleConfigurations == ["a", "%"]
+        dependency9.getDependencyConfigurations("a") == ["a"]
+        dependency9.getDependencyConfigurations("a", "requested") == ["a"]
+        dependency9.getDependencyConfigurations("b") == ["b"]
+        dependency9.getDependencyConfigurations("b", "requested") == ["b"]
+
+        def dependency10 = descriptor.dependencies[9]
+        dependency10.moduleConfigurations == ["a", "*", "!a"]
+        dependency10.getDependencyConfigurations("a") == ["a", "b"]
+        dependency10.getDependencyConfigurations("a", "requested") == ["a", "b"]
+        dependency10.getDependencyConfigurations("b") == ["b"]
+        dependency10.getDependencyConfigurations("b", "requested") == ["b"]
+    }
+
     def verifyFullDependencies(DependencyDescriptor[] dependencies) {
         // no conf def => equivalent to *->*
         DependencyDescriptor dd = getDependency(dependencies, "mymodule2")
diff --git a/subprojects/core-impl/src/test/groovy/org/gradle/api/internal/artifacts/ivyservice/ivyresolve/strategy/ChainVersionMatcherTest.groovy b/subprojects/core-impl/src/test/groovy/org/gradle/api/internal/artifacts/ivyservice/ivyresolve/strategy/ChainVersionMatcherTest.groovy
new file mode 100644
index 0000000..5d09bb0
--- /dev/null
+++ b/subprojects/core-impl/src/test/groovy/org/gradle/api/internal/artifacts/ivyservice/ivyresolve/strategy/ChainVersionMatcherTest.groovy
@@ -0,0 +1,129 @@
+/*
+ * Copyright 2013 the original author or authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY 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.ivyresolve.strategy
+
+import org.gradle.api.artifacts.ModuleVersionIdentifier
+import org.gradle.api.internal.artifacts.ivyservice.ivyresolve.ModuleVersionMetaData
+import spock.lang.Specification
+
+class ChainVersionMatcherTest extends Specification {
+    def chain = new ChainVersionMatcher()
+    def matcher1 = Mock(VersionMatcher)
+    def matcher2 = Mock(VersionMatcher)
+    def matcher3 = Mock(VersionMatcher)
+
+    def setup() {
+        chain.add(matcher1)
+        chain.add(matcher2)
+        chain.add(matcher3)
+    }
+
+    def "doesn't support canHandle method (no known use case)"() {
+        when:
+        chain.canHandle("1")
+
+        then:
+        UnsupportedOperationException e = thrown()
+        e.message.contains("canHandle")
+    }
+
+    def "delegates isDynamic to first matcher that can handle the selector"() {
+        when:
+        def result = chain.isDynamic("1+")
+
+        then:
+        1 * matcher1.canHandle("1+") >> false
+        1 * matcher2.canHandle("1+") >> true
+        1 * matcher2.isDynamic("1+") >> true
+        0 * _
+
+        and:
+        result
+    }
+
+    def "delegates needModuleMetadata to first matcher that can handle the selector"() {
+        when:
+        def result = chain.needModuleMetadata("1+", "2")
+
+        then:
+        1 * matcher1.canHandle("1+") >> false
+        1 * matcher2.canHandle("1+") >> true
+        1 * matcher2.needModuleMetadata("1+", "2") >> false
+        0 * _
+
+        and:
+        !result
+    }
+
+    def "delegates accept to first matcher that can handle the selector"() {
+        when:
+        def result = chain.accept("1+", "2")
+
+        then:
+        1 * matcher1.canHandle("1+") >> false
+        1 * matcher2.canHandle("1+") >> true
+        1 * matcher2.accept("1+", "2") >> false
+        0 * _
+
+        and:
+        !result
+    }
+
+    def "delegates metadata-aware accept to first matcher that can handle the selector"() {
+        def metadata = Stub(ModuleVersionMetaData) {
+            getId() >> Stub(ModuleVersionIdentifier) {
+                getVersion() >> "2"
+            }
+        }
+
+        when:
+        def result = chain.accept("1+", metadata)
+
+        then:
+        1 * matcher1.canHandle("1+") >> false
+        1 * matcher2.canHandle("1+") >> true
+        1 * matcher2.accept("1+", metadata) >> false
+        0 * _
+
+        and:
+        !result
+    }
+
+    def "delegates compare to first matcher that can handle the selector"() {
+        def comparator = Stub(Comparator)
+
+        when:
+        def result = chain.compare("1+", "2")
+
+        then:
+        1 * matcher1.canHandle("1+") >> false
+        1 * matcher2.canHandle("1+") >> true
+        1 * matcher2.compare("1+", "2") >> -3
+        0 * _
+
+        and:
+        result == -3
+    }
+
+    def "complains if a version selector isn't matched by any matcher"() {
+        when:
+        chain.accept("1+", "1")
+
+        then:
+        IllegalArgumentException e = thrown()
+        e.message == "Invalid version selector: 1+"
+    }
+}
diff --git a/subprojects/core-impl/src/test/groovy/org/gradle/api/internal/artifacts/ivyservice/ivyresolve/strategy/ExactVersionMatcherTest.groovy b/subprojects/core-impl/src/test/groovy/org/gradle/api/internal/artifacts/ivyservice/ivyresolve/strategy/ExactVersionMatcherTest.groovy
new file mode 100644
index 0000000..3934423
--- /dev/null
+++ b/subprojects/core-impl/src/test/groovy/org/gradle/api/internal/artifacts/ivyservice/ivyresolve/strategy/ExactVersionMatcherTest.groovy
@@ -0,0 +1,181 @@
+/*
+ * Copyright 2013 the original author or authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY 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.ivyresolve.strategy
+
+import org.gradle.api.artifacts.ModuleVersionIdentifier
+import org.gradle.api.internal.artifacts.ivyservice.ivyresolve.ModuleVersionMetaData
+
+import spock.lang.Specification
+
+class ExactVersionMatcherTest extends Specification {
+    def matcher = new ExactVersionMatcher()
+
+    def "can handle any version selector"() {
+        expect:
+        matcher.canHandle("1.0")
+        matcher.canHandle("[1.0,2.0]")
+        matcher.canHandle("!@#%")
+    }
+
+    def "considers selector as static"() {
+        expect:
+        !matcher.isDynamic("1.0")
+        !matcher.isDynamic("[1.0,2.0]")
+    }
+
+    def "doesn't need metadata"() {
+        expect:
+        !matcher.needModuleMetadata("1.0", "1.0")
+        !matcher.needModuleMetadata("[1.0,2.0]", "2.0")
+    }
+
+    def "accepts candidate version iff it literally matches the selector"() {
+        expect:
+        matcher.accept("", "")
+        matcher.accept("1.0", "1.0")
+        matcher.accept("2.0", "2.0")
+        matcher.accept("!@#%", "!@#%")
+        matcher.accept("hey joe", "hey joe")
+        !matcher.accept("1.0", "1.1")
+        !matcher.accept("2.0", "3.0")
+        !matcher.accept("!@#%", "%#@!")
+        !matcher.accept("hey joe", "hoe hey")
+    }
+
+    def "does not accept candidate version that differs in separator"() {
+        expect:
+        !matcher.accept("1.0", "1_0")
+        !matcher.accept("1_0", "1-0")
+        !matcher.accept("1-0", "1+0")
+        !matcher.accept("1.a.2", "1a2")
+    }
+
+    def "does not accept candidate version that has different number of trailing .0's"() {
+        expect:
+        !matcher.accept("1.0.0", "1.0")
+        !matcher.accept("1", "1.0.0")
+    }
+
+    def "does not accept candidate version that has different capitalization"() {
+        !matcher.accept("1.0-alpha", "1.0-ALPHA")
+    }
+
+    def "supports metadata-aware accept method (with same result)"() {
+        def metadata = Stub(ModuleVersionMetaData) {
+            getId() >> Stub(ModuleVersionIdentifier) {
+                getVersion() >> metadataVersion
+            }
+        }
+
+        expect:
+        matcher.accept("1.0", metadata) == result
+
+        where:
+        metadataVersion | result
+        "1.0"           | true
+        "2.0"           | false
+    }
+
+    def "compares versions lexicographically"() {
+        expect:
+        matcher.compare(v1, v2) < 0
+        matcher.compare(v2, v1) > 0
+        matcher.compare(v1, v1) == 0
+        matcher.compare(v2, v2) == 0
+
+        where:
+        v1          | v2
+        "1.0"       | "2.0"
+        "1.0"       | "1.1"
+        "1.0.1"     | "1.1.0"
+        "1.2"       | "1.2.3"
+        "1.0-1"     | "1.0-2"
+        "1.0.a"     | "1.0.b"
+        "1.0-alpha" | "1.0"
+        "1.0-alpha" | "1.0-beta"
+        "1.0.alpha" | "1.0.b"
+    }
+
+    def "gives some special treatment to 'dev', 'rc', and 'final' qualifiers"() {
+        expect:
+        matcher.compare(v1, v2) < 0
+        matcher.compare(v2, v1) > 0
+        matcher.compare(v1, v1) == 0
+        matcher.compare(v2, v2) == 0
+
+        where:
+        v1          | v2
+        "1.0-dev-1" | "1.0"
+        "1.0-dev-1" | "1.0-dev-2"
+        "1.0-rc-1"  | "1.0"
+        "1.0-rc-1"  | "1.0-rc-2"
+        "1.0-final" | "1.0"
+        "1.0-dev-1" | "1.0-rc-1"
+        "1.0-rc-1"  | "1.0-final"
+        "1.0-dev-1" | "1.0-final"
+    }
+
+    def "compares identical versions equal"() {
+        expect:
+        matcher.compare(v1, v2) == 0
+        matcher.compare(v2, v1) == 0
+
+        where:
+        v1        | v2
+        ""        | ""
+        "1"       | "1"
+        "1.0.0"   | "1.0.0"
+        "!@#%"    | "!@#%"
+        "hey joe" | "hey joe"
+    }
+
+    def "compares versions that differ only in separators equal"() {
+        expect:
+        matcher.compare("1.0", "1_0") == 0
+        matcher.compare("1_0", "1-0") == 0
+        matcher.compare("1-0", "1+0") == 0
+        matcher.compare("1.a.2", "1a2") == 0 // number-word and word-number boundaries are considered separators
+    }
+
+    def "compares unrelated versions unequal"() {
+        expect:
+        matcher.compare("1.0", "") != 0
+        matcher.compare("1.0", "!@#%") != 0
+        matcher.compare("1.0", "hey joe") != 0
+    }
+
+    // original Ivy behavior - should we change it?
+    def "does not compare versions with different number of trailing .0's equal"() {
+        expect:
+        matcher.compare(v1, v2) > 0
+        matcher.compare(v2, v1) < 0
+
+        where:
+        v1          | v2
+        "1.0.0"     | "1.0"
+        "1.0.0"     | "1"
+    }
+
+    def "does not compare versions with different capitalization equal"() {
+        expect:
+        matcher.compare(v1, v2) > 0
+        matcher.compare(v2, v1) < 0
+
+        where:
+        v1          | v2
+        "1.0-alpha" | "1.0-ALPHA"
+    }
+}
diff --git a/subprojects/core-impl/src/test/groovy/org/gradle/api/internal/artifacts/ivyservice/ivyresolve/strategy/LatestVersionMatcherTest.groovy b/subprojects/core-impl/src/test/groovy/org/gradle/api/internal/artifacts/ivyservice/ivyresolve/strategy/LatestVersionMatcherTest.groovy
new file mode 100644
index 0000000..11c530f
--- /dev/null
+++ b/subprojects/core-impl/src/test/groovy/org/gradle/api/internal/artifacts/ivyservice/ivyresolve/strategy/LatestVersionMatcherTest.groovy
@@ -0,0 +1,83 @@
+/*
+ * Copyright 2013 the original author or authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY 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.ivyresolve.strategy
+
+import org.gradle.api.internal.artifacts.ivyservice.ivyresolve.ModuleVersionMetaData
+import spock.lang.Specification
+
+class LatestVersionMatcherTest extends Specification {
+    def matcher = new LatestVersionMatcher()
+
+    def "handles selectors starting with 'latest.'"() {
+        expect:
+        matcher.canHandle("latest.integration")
+        matcher.canHandle("latest.foo")
+        matcher.canHandle("latest.123")
+        !matcher.canHandle("1.0")
+        !matcher.canHandle("[1.0,2.0]")
+    }
+
+    def "all handled selectors are dynamic"() {
+        expect:
+        matcher.isDynamic("latest.integration")
+        matcher.isDynamic("latest.foo")
+        matcher.isDynamic("latest.123")
+    }
+
+    def "always needs metadata"() {
+        expect:
+        matcher.needModuleMetadata("latest.integration", "1.0")
+        matcher.needModuleMetadata("latest.foo", "1.0")
+        matcher.needModuleMetadata("latest.123", "1.0")
+    }
+
+    def "only supports metadata-aware accept method"() {
+        when:
+        matcher.accept("latest.integration", "1.0")
+
+        then:
+        UnsupportedOperationException e = thrown()
+        e.message.contains("accept")
+    }
+
+    def "accepts a candidate version iff its status is equal to or higher than the selector's status"() {
+        def metadata = Stub(ModuleVersionMetaData) {
+            getStatus() >> "silver"
+            getStatusScheme() >> ["bronze", "silver", "gold"]
+        }
+
+        expect:
+        matcher.accept("latest.bronze", metadata)
+        matcher.accept("latest.silver", metadata)
+        !matcher.accept("latest.gold", metadata)
+    }
+
+    def "rejects a candidate version if selector's status is not contained in candidate's status scheme"() {
+        def metadata = Stub(ModuleVersionMetaData) {
+            getStatus() >> "silver"
+            getStatusScheme() >> ["bronze", "silver", "gold"]
+        }
+
+        expect:
+        !matcher.accept("latest.other", metadata)
+    }
+
+    def "cannot tell which of version selector and candidate version is greater"() {
+        expect:
+        matcher.compare("latest.integration", "1.0") == 0
+        matcher.compare("latest.release", "2.0") == 0
+    }
+}
diff --git a/subprojects/core-impl/src/test/groovy/org/gradle/api/internal/artifacts/ivyservice/ivyresolve/strategy/LatestVersionStrategyTest.groovy b/subprojects/core-impl/src/test/groovy/org/gradle/api/internal/artifacts/ivyservice/ivyresolve/strategy/LatestVersionStrategyTest.groovy
new file mode 100644
index 0000000..36db721
--- /dev/null
+++ b/subprojects/core-impl/src/test/groovy/org/gradle/api/internal/artifacts/ivyservice/ivyresolve/strategy/LatestVersionStrategyTest.groovy
@@ -0,0 +1,78 @@
+/*
+ * Copyright 2013 the original author or authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY 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.ivyresolve.strategy
+
+import org.gradle.api.internal.artifacts.ivyservice.ivyresolve.VersionInfo
+
+import spock.lang.Specification
+
+class LatestVersionStrategyTest extends Specification {
+    def chain = new ChainVersionMatcher()
+    def strategy = new LatestVersionStrategy(chain)
+    def matcher = Mock(VersionMatcher)
+
+    def setup() {
+        chain.add(new SubVersionMatcher(new ExactVersionMatcher()))
+        chain.add(matcher)
+        chain.add(new ExactVersionMatcher())
+    }
+
+    def "compares static versions according to version matcher"() {
+        expect:
+        strategy.compare(new VersionInfo("1.0"), new VersionInfo("2.0")) < 0
+        strategy.compare(new VersionInfo("1.0"), new VersionInfo("1.0")) == 0
+        strategy.compare(new VersionInfo("2.0"), new VersionInfo("1.0")) > 0
+    }
+
+    def "compares dynamic and static version according to version matcher"() {
+        expect:
+        strategy.compare(new VersionInfo("1.+"), new VersionInfo("2.0")) < 0
+        strategy.compare(new VersionInfo("2.0"), new VersionInfo("1.+")) > 0
+        strategy.compare(new VersionInfo("1.+"), new VersionInfo("1.11")) > 0
+        strategy.compare(new VersionInfo("1.11"), new VersionInfo("1.+")) < 0
+    }
+
+    def "considers dynamic version greater if it compares equal according to version matcher"() {
+        matcher.canHandle("foo") >> true
+        matcher.isDynamic("foo") >> true
+        matcher.isDynamic("1.0") >> false
+        matcher.compare(_, _) >> 0
+
+        expect:
+        strategy.compare(new VersionInfo("foo"), new VersionInfo("1.0")) > 0
+        strategy.compare(new VersionInfo("1.0"), new VersionInfo("foo")) < 0
+    }
+
+    def "sorts elements in ascending order according to version matcher"() {
+        def version1 = new VersionInfo("1.0")
+        def version2 = new VersionInfo("1.+")
+        def version3 = new VersionInfo("2.2")
+        def version4 = new VersionInfo("2.+")
+
+        expect:
+        strategy.sort([version3, version1, version2, version4]) == [version1, version2, version3, version4]
+    }
+
+    def "finds latest element according to version matcher"() {
+        def version1 = new VersionInfo("1.0")
+        def version2 = new VersionInfo("1.+")
+        def version3 = new VersionInfo("2.2")
+        def version4 = new VersionInfo("2.+")
+
+        expect:
+        strategy.findLatest([version3, version1, version2, version4]) == version4
+    }
+}
diff --git a/subprojects/core-impl/src/test/groovy/org/gradle/api/internal/artifacts/ivyservice/ivyresolve/strategy/SubVersionMatcherTest.groovy b/subprojects/core-impl/src/test/groovy/org/gradle/api/internal/artifacts/ivyservice/ivyresolve/strategy/SubVersionMatcherTest.groovy
new file mode 100644
index 0000000..64c1969
--- /dev/null
+++ b/subprojects/core-impl/src/test/groovy/org/gradle/api/internal/artifacts/ivyservice/ivyresolve/strategy/SubVersionMatcherTest.groovy
@@ -0,0 +1,84 @@
+/*
+ * Copyright 2013 the original author or authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY 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.ivyresolve.strategy
+
+import org.gradle.api.artifacts.ModuleVersionIdentifier
+import org.gradle.api.internal.artifacts.ivyservice.ivyresolve.ModuleVersionMetaData
+
+import spock.lang.Specification
+
+class SubVersionMatcherTest extends Specification {
+    def matcher = new SubVersionMatcher(new ExactVersionMatcher())
+
+    def "handles selectors that end in '+'"() {
+        expect:
+        matcher.canHandle("1+")
+        matcher.canHandle("1.2.3+")
+        !matcher.canHandle("1")
+        !matcher.canHandle("1.+.3")
+    }
+
+    def "all handled selectors are dynamic"() {
+        expect:
+        matcher.isDynamic("1+")
+        matcher.isDynamic("1.2.3+")
+    }
+
+    def "never needs metadata"() {
+        expect:
+        !matcher.needModuleMetadata("1+", "1")
+        !matcher.needModuleMetadata("1.2.3+", "1.2.3")
+    }
+
+    def "accepts candidate versions that literally match the selector up until the trailing '+'"() {
+        expect:
+        matcher.accept("1+", "11")
+        matcher.accept("1.+", "1.2")
+        matcher.accept("1.2.3+", "1.2.3.11")
+        !matcher.accept("1+", "2")
+        !matcher.accept("1.+", "11")
+        !matcher.accept("1.2.3+", "1.2")
+    }
+
+    def "metadata-aware accept method delivers same results"() {
+        def metadata = Stub(ModuleVersionMetaData) {
+            getId() >> Stub(ModuleVersionIdentifier) {
+                getVersion() >> metadataVersion
+            }
+        }
+
+        expect:
+        matcher.accept("1.+", metadata) == result
+
+        where:
+        metadataVersion | result
+        "1.5"           | true
+        "2.5"           | false
+    }
+
+    def "considers a '+' selector greater than any matching candidate version"() {
+        expect:
+        matcher.compare("1+", "11") > 0
+        matcher.compare("1.+", "1.2") > 0
+        matcher.compare("1.2.3+", "1.2.3.11") > 0
+    }
+
+    def "falls back to the provided comparator if selector doesn't match candidate version"() {
+        expect:
+        matcher.compare("1+", "2") < 0
+        matcher.compare("1+", "0.5") > 0
+    }
+}
diff --git a/subprojects/core-impl/src/test/groovy/org/gradle/api/internal/artifacts/ivyservice/ivyresolve/strategy/VersionRangeMatcherTest.groovy b/subprojects/core-impl/src/test/groovy/org/gradle/api/internal/artifacts/ivyservice/ivyresolve/strategy/VersionRangeMatcherTest.groovy
new file mode 100644
index 0000000..61bf399
--- /dev/null
+++ b/subprojects/core-impl/src/test/groovy/org/gradle/api/internal/artifacts/ivyservice/ivyresolve/strategy/VersionRangeMatcherTest.groovy
@@ -0,0 +1,208 @@
+/*
+ * Copyright 2013 the original author or authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY 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.ivyresolve.strategy
+
+import org.gradle.api.artifacts.ModuleVersionIdentifier
+import org.gradle.api.internal.artifacts.ivyservice.ivyresolve.ModuleVersionMetaData
+
+import spock.lang.Specification
+
+public class VersionRangeMatcherTest extends Specification {
+    def matcher = new VersionRangeMatcher(new ExactVersionMatcher())
+
+    def "handles selectors that use valid version range syntax"() {
+        expect:
+        matcher.canHandle("[1.0,2.0]")
+        matcher.canHandle("[1.0,2.0[")
+        matcher.canHandle("]1.0,2.0]")
+        matcher.canHandle("]1.0,2.0[")
+        matcher.canHandle("[1.0,)")
+        matcher.canHandle("]1.0,)")
+        matcher.canHandle("(,2.0]")
+        matcher.canHandle("(,2.0[")
+    }
+
+    def "does not handle selectors that use no or invalid version range syntax"() {
+        expect:
+        !matcher.canHandle("1")
+        !matcher.canHandle("1+")
+        !matcher.canHandle("[1")
+        !matcher.canHandle("[]")
+        !matcher.canHandle("[1,2,3]")
+    }
+
+    def "all handled selectors are dynamic"() {
+        expect:
+        matcher.isDynamic("[1.0,2.0]")
+        matcher.isDynamic("[1.0,)")
+    }
+
+    def "never needs metadata"() {
+        expect:
+        !matcher.needModuleMetadata("[1.0,2.0]", "1.0")
+        !matcher.needModuleMetadata("[1.0,)", "1.0")
+        !matcher.needModuleMetadata("1", "1")
+    }
+
+    def "accepts candidate versions that fall into the selector's range"() {
+        expect:
+        matcher.accept("[1.0,2.0]", "1.0")
+        matcher.accept("[1.0,2.0]", "1.2.3")
+        matcher.accept("[1.0,2.0]", "2.0")
+
+        matcher.accept("[1.0,2.0[", "1.0")
+        matcher.accept("[1.0,2.0[", "1.2.3")
+        matcher.accept("[1.0,2.0[", "1.99")
+
+        matcher.accept("]1.0,2.0]", "1.0.1")
+        matcher.accept("]1.0,2.0]", "1.2.3")
+        matcher.accept("]1.0,2.0]", "2.0")
+
+        matcher.accept("]1.0,2.0[", "1.0.1")
+        matcher.accept("]1.0,2.0[", "1.2.3")
+        matcher.accept("]1.0,2.0[", "1.99")
+
+        matcher.accept("[1.0,)", "1.0")
+        matcher.accept("[1.0,)", "1.2.3")
+        matcher.accept("[1.0,)", "2.3.4")
+
+        matcher.accept("]1.0,)", "1.0.1")
+        matcher.accept("]1.0,)", "1.2.3")
+        matcher.accept("]1.0,)", "2.3.4")
+
+        matcher.accept("(,2.0]", "0")
+        matcher.accept("(,2.0]", "0.1.2")
+        matcher.accept("(,2.0]", "2.0")
+
+        matcher.accept("(,2.0[", "0")
+        matcher.accept("(,2.0[", "0.1.2")
+        matcher.accept("(,2.0[", "1.99")
+    }
+
+    def "accepts candidate versions that fall into the selector's range (adding qualifiers to the mix)"() {
+        expect:
+        matcher.accept("[1.0,2.0]", "1.5-dev-1")
+        matcher.accept("[1.0,2.0]", "1.2.3-rc-2")
+        matcher.accept("[1.0,2.0]", "2.0-final")
+
+        matcher.accept("[1.0-dev-1,2.0[", "1.0")
+        matcher.accept("[1.0,2.0-rc-2[", "2.0-rc-1")
+        matcher.accept("[1.0,2.0[", "2.0-final")
+
+        matcher.accept("]1.0-dev-1,2.0]", "1.0")
+        matcher.accept("]1.0-rc-2,2.0]", "1.0-rc-3")
+
+        matcher.accept("]1.0-dev-1,1.0-dev-3[", "1.0-dev-2")
+        matcher.accept("]1.0-dev-1,1.0-rc-1[", "1.0-dev-99")
+    }
+
+    def "rejects candidate versions that don't fall into the selector's range"() {
+        expect:
+        !matcher.accept("[1.0,2.0]", "0.99")
+        !matcher.accept("[1.0,2.0]", "2.0.1")
+        !matcher.accept("[1.0,2.0]", "42")
+
+        !matcher.accept("[1.0,2.0[", "0.99")
+        !matcher.accept("[1.0,2.0[", "2.0")
+        !matcher.accept("[1.0,2.0[", "42")
+
+        !matcher.accept("]1.0,2.0]", "1.0")
+        !matcher.accept("]1.0,2.0]", "2.0.1")
+        !matcher.accept("]1.0,2.0]", "42")
+
+        !matcher.accept("]1.0,2.0[", "1.0")
+        !matcher.accept("]1.0,2.0[", "2.0")
+        !matcher.accept("]1.0,2.0[", "42")
+
+        !matcher.accept("[1.0,)", "0")
+        !matcher.accept("[1.0,)", "0.99")
+
+        !matcher.accept("]1.0,)", "0")
+        !matcher.accept("]1.0,)", "1")
+        !matcher.accept("]1.0,)", "1.0")
+
+        !matcher.accept("(,2.0]", "2.0.1")
+        !matcher.accept("(,2.0]", "42")
+
+        !matcher.accept("(,2.0[", "2.0")
+        !matcher.accept("(,2.0[", "42")
+    }
+
+    def "rejects candidate versions that don't fall into the selector's range (adding qualifiers to the mix)"() {
+        expect:
+        !matcher.accept("[1.0,2.0]", "2.5-dev-1")
+        !matcher.accept("[1.0,2.0]", "1.0-rc-2")
+        !matcher.accept("[1.0,2.0]", "1.0-final")
+
+        !matcher.accept("[1.0-dev-2,2.0[", "1.0-dev-1")
+        !matcher.accept("[1.0,2.0-rc-2[", "2.0-rc-2")
+        !matcher.accept("[1.0,2.0-final[", "2.0")
+
+        !matcher.accept("]1.0-dev-1,2.0]", "1.0-dev-1")
+        !matcher.accept("]1.0-rc-2,2.0]", "1.0-dev-3")
+
+        !matcher.accept("]1.0-dev-1,1.0-dev-3[", "1.0-dev-3")
+        !matcher.accept("]1.0-dev-1,1.0-rc-1[", "1.0-final-0")
+    }
+
+    def "metadata-aware accept method delivers same results"() {
+        def metadata = Stub(ModuleVersionMetaData) {
+            getId() >> Stub(ModuleVersionIdentifier) {
+                getVersion() >> metadataVersion
+            }
+        }
+
+        expect:
+        matcher.accept("[1.0,2.0]", metadata) == result
+
+        where:
+        metadataVersion | result
+        "1.5"           | true
+        "2.5"           | false
+    }
+
+    def "compares candidate versions against the selector's upper bound"() {
+        expect:
+        matcher.compare(range, "0.5") > 0
+        matcher.compare(range, "1.0") > 0
+        matcher.compare(range, "1.5") > 0
+        matcher.compare(range, "2.0") < 0 // unsure why [1.0,2.0] isn't considered equal to 2.0 (apparently never returns 0)
+        matcher.compare(range, "2.5") < 0
+
+        where:
+        range       | _
+        "[1.0,2.0]" | _
+        "[1.0,2.0[" | _
+        "]1.0,2.0]" | _
+        "]1.0,2.0[" | _
+        "(,2.0]"    | _
+        "(,2.0["    | _
+    }
+
+    def "selectors with infinite upper bound compare greater than any candidate version"() {
+        expect:
+        matcher.compare(range, "0.5") > 0
+        matcher.compare(range, "1.0") > 0
+        matcher.compare(range, "1.5") > 0
+        matcher.compare(range, "2.0") > 0
+        matcher.compare(range, "2.5") > 0
+
+        where:
+        range    | _
+        "[1.0,)" | _
+        "]1.0,)" | _
+    }
+}
diff --git a/subprojects/core-impl/src/test/groovy/org/gradle/api/internal/artifacts/ivyservice/modulecache/ModuleDescriptorStoreTest.groovy b/subprojects/core-impl/src/test/groovy/org/gradle/api/internal/artifacts/ivyservice/modulecache/ModuleDescriptorStoreTest.groovy
index b5f32cb..aaf4b4f 100644
--- a/subprojects/core-impl/src/test/groovy/org/gradle/api/internal/artifacts/ivyservice/modulecache/ModuleDescriptorStoreTest.groovy
+++ b/subprojects/core-impl/src/test/groovy/org/gradle/api/internal/artifacts/ivyservice/modulecache/ModuleDescriptorStoreTest.groovy
@@ -19,11 +19,12 @@ package org.gradle.api.internal.artifacts.ivyservice.modulecache
 import org.apache.ivy.core.module.descriptor.ModuleDescriptor
 import org.apache.ivy.core.module.id.ModuleRevisionId
 import org.gradle.api.artifacts.ModuleVersionIdentifier
+import org.gradle.api.internal.artifacts.ivyservice.DependencyToModuleVersionResolver
 import org.gradle.api.internal.artifacts.ivyservice.IvyModuleDescriptorWriter
 import org.gradle.api.internal.artifacts.ivyservice.ivyresolve.ModuleVersionRepository
 import org.gradle.api.internal.artifacts.ivyservice.ivyresolve.parser.IvyXmlModuleDescriptorParser
-import org.gradle.api.internal.filestore.FileStoreEntry
 import org.gradle.api.internal.filestore.PathKeyFileStore
+import org.gradle.internal.resource.local.LocallyAvailableResource
 import org.gradle.test.fixtures.file.TestNameTestDirectoryProvider
 import org.junit.Rule
 import spock.lang.Specification
@@ -35,11 +36,12 @@ class ModuleDescriptorStoreTest extends Specification {
     PathKeyFileStore pathKeyFileStore = Mock()
     ModuleRevisionId moduleRevisionId = Mock()
     ModuleVersionRepository repository = Mock()
-    FileStoreEntry fileStoreEntry = Mock()
+    LocallyAvailableResource fileStoreEntry = Mock()
     ModuleDescriptor moduleDescriptor = Mock()
     IvyModuleDescriptorWriter ivyModuleDescriptorWriter = Mock()
     IvyXmlModuleDescriptorParser ivyXmlModuleDescriptorParser = Mock()
     ModuleVersionIdentifier moduleVersionIdentifier = Mock()
+    def resolver = Mock(DependencyToModuleVersionResolver)
 
     def setup() {
         store = new ModuleDescriptorStore(pathKeyFileStore, ivyModuleDescriptorWriter, ivyXmlModuleDescriptorParser);
@@ -54,12 +56,12 @@ class ModuleDescriptorStoreTest extends Specification {
         when:
         pathKeyFileStore.get("module-metadata/org.test/testArtifact/1.0/repositoryId/ivy.xml") >> null
         then:
-        null == store.getModuleDescriptor(repository, moduleVersionIdentifier)
+        null == store.getModuleDescriptor(repository, moduleVersionIdentifier, resolver)
     }
 
     def "getModuleDescriptorFile uses PathKeyFileStore to get file"() {
         when:
-        store.getModuleDescriptor(repository, moduleVersionIdentifier);
+        store.getModuleDescriptor(repository, moduleVersionIdentifier, resolver);
         then:
         1 * pathKeyFileStore.get("module-metadata/org.test/testArtifact/1.0/repositoryId/ivy.xml") >> null
     }
diff --git a/subprojects/core-impl/src/test/groovy/org/gradle/api/internal/artifacts/ivyservice/moduleconverter/DefaultArtifactsToModuleDescriptorConverterTest.java b/subprojects/core-impl/src/test/groovy/org/gradle/api/internal/artifacts/ivyservice/moduleconverter/DefaultArtifactsToModuleDescriptorConverterTest.java
index 8e973ca..79b2d02 100644
--- a/subprojects/core-impl/src/test/groovy/org/gradle/api/internal/artifacts/ivyservice/moduleconverter/DefaultArtifactsToModuleDescriptorConverterTest.java
+++ b/subprojects/core-impl/src/test/groovy/org/gradle/api/internal/artifacts/ivyservice/moduleconverter/DefaultArtifactsToModuleDescriptorConverterTest.java
@@ -19,31 +19,29 @@ import org.apache.ivy.core.module.descriptor.Artifact;
 import org.apache.ivy.core.module.descriptor.DefaultArtifact;
 import org.apache.ivy.core.module.descriptor.DefaultModuleDescriptor;
 import org.apache.ivy.core.module.descriptor.ModuleDescriptor;
-import org.gradle.api.artifacts.PublishArtifactSet;
 import org.gradle.api.artifacts.Configuration;
 import org.gradle.api.artifacts.Dependency;
 import org.gradle.api.artifacts.PublishArtifact;
-import org.gradle.api.internal.artifacts.ivyservice.DefaultIvyDependencyPublisher;
+import org.gradle.api.artifacts.PublishArtifactSet;
+import org.gradle.api.internal.artifacts.BuildableModuleVersionPublishMetaData;
 import org.gradle.api.internal.artifacts.publish.DefaultPublishArtifact;
-import org.gradle.util.HelperUtil;
+import org.gradle.util.TestUtil;
 import org.gradle.util.JUnit4GroovyMockery;
 import org.gradle.util.WrapUtil;
-import static org.hamcrest.Matchers.equalTo;
 import org.jmock.Expectations;
 import org.jmock.integration.junit4.JMock;
 import org.jmock.integration.junit4.JUnit4Mockery;
-import static org.junit.Assert.assertThat;
 import org.junit.Test;
 import org.junit.runner.RunWith;
 
 import java.io.File;
 import java.util.Date;
-import java.util.HashMap;
 import java.util.Map;
 
-/**
- * @author Hans Dockter
- */
+import static org.hamcrest.Matchers.equalTo;
+import static org.hamcrest.Matchers.notNullValue;
+import static org.junit.Assert.assertThat;
+
 @RunWith(JMock.class)
 public class DefaultArtifactsToModuleDescriptorConverterTest {
     private JUnit4Mockery context = new JUnit4GroovyMockery();
@@ -54,53 +52,32 @@ public class DefaultArtifactsToModuleDescriptorConverterTest {
         Configuration configurationStub1 = createConfigurationStub(publishArtifactConf1);
         final PublishArtifact publishArtifactConf2 = createNamedPublishArtifact("conf2");
         Configuration configurationStub2 = createConfigurationStub(publishArtifactConf2);
-        final ArtifactsExtraAttributesStrategy artifactsExtraAttributesStrategyMock = context.mock(ArtifactsExtraAttributesStrategy.class);
-        final Map<String, String> extraAttributesArtifact1 = WrapUtil.toMap("name", publishArtifactConf1.getName());
-        final Map<String, String> extraAttributesArtifact2 = WrapUtil.toMap("name", publishArtifactConf2.getName());
+        final DefaultModuleDescriptor moduleDescriptor = TestUtil.createModuleDescriptor(WrapUtil.toSet(configurationStub1.getName(),
+                configurationStub2.getName()));
+        final BuildableModuleVersionPublishMetaData publishMetaData = context.mock(BuildableModuleVersionPublishMetaData.class);
         context.checking(new Expectations() {{
-            one(artifactsExtraAttributesStrategyMock).createExtraAttributes(publishArtifactConf1);
-            will(returnValue(extraAttributesArtifact1));
-            one(artifactsExtraAttributesStrategyMock).createExtraAttributes(publishArtifactConf2);
-            will(returnValue(extraAttributesArtifact2));
+            allowing(publishMetaData).getModuleDescriptor();
+            will(returnValue(moduleDescriptor));
+            exactly(2).of(publishMetaData).addArtifact(with(notNullValue(Artifact.class)), with(notNullValue(File.class)));
         }});
-        DefaultModuleDescriptor moduleDescriptor = HelperUtil.createModuleDescriptor(WrapUtil.toSet(configurationStub1.getName(),
-                configurationStub2.getName()));
 
-        DefaultArtifactsToModuleDescriptorConverter artifactsToModuleDescriptorConverter =
-                new DefaultArtifactsToModuleDescriptorConverter(artifactsExtraAttributesStrategyMock);
+        DefaultArtifactsToModuleDescriptorConverter artifactsToModuleDescriptorConverter = new DefaultArtifactsToModuleDescriptorConverter();
 
-        artifactsToModuleDescriptorConverter.addArtifacts(moduleDescriptor, WrapUtil.toSet(configurationStub1, configurationStub2));
+        artifactsToModuleDescriptorConverter.addArtifacts(publishMetaData, WrapUtil.toSet(configurationStub1, configurationStub2));
 
-        assertArtifactIsAdded(configurationStub1, moduleDescriptor, extraAttributesArtifact1);
-        assertArtifactIsAdded(configurationStub2, moduleDescriptor, extraAttributesArtifact2);
+        assertArtifactIsAdded(configurationStub1, moduleDescriptor);
+        assertArtifactIsAdded(configurationStub2, moduleDescriptor);
         assertThat(moduleDescriptor.getAllArtifacts().length, equalTo(2));
     }
 
-    @Test
-    public void testIvyFileStrategy() {
-        assertThat(
-                DefaultArtifactsToModuleDescriptorConverter.IVY_FILE_STRATEGY.createExtraAttributes(context.mock(PublishArtifact.class)),
-                equalTo((Map) new HashMap<String, String>()));
-    }
-
-    @Test
-    public void testResolveStrategy() {
-        PublishArtifact publishArtifact = createNamedPublishArtifact("someName");
-        Map<String, String> expectedExtraAttributes = WrapUtil.toMap(DefaultIvyDependencyPublisher.FILE_ABSOLUTE_PATH_EXTRA_ATTRIBUTE, publishArtifact.getFile().getAbsolutePath());
-        assertThat(
-                DefaultArtifactsToModuleDescriptorConverter.RESOLVE_STRATEGY.createExtraAttributes(publishArtifact),
-                equalTo(expectedExtraAttributes));
-    }
-
-    private void assertArtifactIsAdded(Configuration configuration, DefaultModuleDescriptor moduleDescriptor, Map<String, String> extraAttributes) {
+    private void assertArtifactIsAdded(Configuration configuration, DefaultModuleDescriptor moduleDescriptor) {
         assertThat(moduleDescriptor.getArtifacts(configuration.getName()),
-                equalTo(WrapUtil.toArray(expectedIvyArtifact(configuration, moduleDescriptor, extraAttributes))));
+                equalTo(WrapUtil.toArray(expectedIvyArtifact(configuration, moduleDescriptor))));
     }
 
-    private Artifact expectedIvyArtifact(Configuration configuration, ModuleDescriptor moduleDescriptor, Map<String, String> additionalExtraAttributes) {
+    private Artifact expectedIvyArtifact(Configuration configuration, ModuleDescriptor moduleDescriptor) {
         PublishArtifact publishArtifact = configuration.getArtifacts().iterator().next();
         Map<String, String> extraAttributes = WrapUtil.toMap(Dependency.CLASSIFIER, publishArtifact.getClassifier());
-        extraAttributes.putAll(additionalExtraAttributes);
         return new DefaultArtifact(moduleDescriptor.getModuleRevisionId(),
                 publishArtifact.getDate(),
                 publishArtifact.getName(),
diff --git a/subprojects/core-impl/src/test/groovy/org/gradle/api/internal/artifacts/ivyservice/moduleconverter/DefaultConfigurationsToModuleDescriptorConverterTest.java b/subprojects/core-impl/src/test/groovy/org/gradle/api/internal/artifacts/ivyservice/moduleconverter/DefaultConfigurationsToModuleDescriptorConverterTest.java
index 3b27122..69688de 100644
--- a/subprojects/core-impl/src/test/groovy/org/gradle/api/internal/artifacts/ivyservice/moduleconverter/DefaultConfigurationsToModuleDescriptorConverterTest.java
+++ b/subprojects/core-impl/src/test/groovy/org/gradle/api/internal/artifacts/ivyservice/moduleconverter/DefaultConfigurationsToModuleDescriptorConverterTest.java
@@ -18,21 +18,19 @@ package org.gradle.api.internal.artifacts.ivyservice.moduleconverter;
 import org.apache.ivy.core.module.descriptor.DefaultModuleDescriptor;
 import org.gradle.api.artifacts.Configuration;
 import org.gradle.api.internal.artifacts.configurations.Configurations;
-import org.gradle.util.HelperUtil;
+import org.gradle.util.TestUtil;
 import org.gradle.util.WrapUtil;
-import static org.hamcrest.Matchers.equalTo;
 import org.jmock.Expectations;
 import org.jmock.integration.junit4.JMock;
 import org.jmock.integration.junit4.JUnit4Mockery;
-import static org.junit.Assert.assertThat;
 import org.junit.Test;
 import org.junit.runner.RunWith;
 
 import java.util.Collections;
 
-/**
- * @author Hans Dockter
- */
+import static org.hamcrest.Matchers.equalTo;
+import static org.junit.Assert.assertThat;
+
 @RunWith(JMock.class)
 public class DefaultConfigurationsToModuleDescriptorConverterTest {
     private DefaultConfigurationsToModuleDescriptorConverter configurationsToModuleDescriptorConverter = new DefaultConfigurationsToModuleDescriptorConverter();
@@ -43,7 +41,7 @@ public class DefaultConfigurationsToModuleDescriptorConverterTest {
     public void testAddConfigurations() {
         Configuration configurationStub1 = createNamesAndExtendedConfigurationStub("conf1");
         Configuration configurationStub2 = createNamesAndExtendedConfigurationStub("conf2", configurationStub1);
-        final DefaultModuleDescriptor moduleDescriptor = HelperUtil.createModuleDescriptor(Collections.EMPTY_SET);
+        final DefaultModuleDescriptor moduleDescriptor = TestUtil.createModuleDescriptor(Collections.EMPTY_SET);
 
         configurationsToModuleDescriptorConverter.addConfigurations(moduleDescriptor, WrapUtil.toSet(configurationStub1, configurationStub2));
 
@@ -80,7 +78,7 @@ public class DefaultConfigurationsToModuleDescriptorConverterTest {
             will(returnValue(true));
 
             allowing(configurationStub).getDescription();
-            will(returnValue(HelperUtil.createUniqueId()));
+            will(returnValue(TestUtil.createUniqueId()));
 
             allowing(configurationStub).isVisible();
             will(returnValue(true));
diff --git a/subprojects/core-impl/src/test/groovy/org/gradle/api/internal/artifacts/ivyservice/moduleconverter/DefaultExcludeRuleConverterTest.java b/subprojects/core-impl/src/test/groovy/org/gradle/api/internal/artifacts/ivyservice/moduleconverter/DefaultExcludeRuleConverterTest.java
index 6af0160..61df00e 100644
--- a/subprojects/core-impl/src/test/groovy/org/gradle/api/internal/artifacts/ivyservice/moduleconverter/DefaultExcludeRuleConverterTest.java
+++ b/subprojects/core-impl/src/test/groovy/org/gradle/api/internal/artifacts/ivyservice/moduleconverter/DefaultExcludeRuleConverterTest.java
@@ -24,9 +24,6 @@ import org.junit.Test;
 
 import static org.junit.Assert.assertThat;
 
-/**
- * @author Hans Dockter
- */
 public class DefaultExcludeRuleConverterTest {
 
     @Test
diff --git a/subprojects/core-impl/src/test/groovy/org/gradle/api/internal/artifacts/ivyservice/moduleconverter/DefaultModuleDescriptorFactoryTest.groovy b/subprojects/core-impl/src/test/groovy/org/gradle/api/internal/artifacts/ivyservice/moduleconverter/DefaultModuleDescriptorFactoryTest.groovy
index 808f5cc..49ec853 100644
--- a/subprojects/core-impl/src/test/groovy/org/gradle/api/internal/artifacts/ivyservice/moduleconverter/DefaultModuleDescriptorFactoryTest.groovy
+++ b/subprojects/core-impl/src/test/groovy/org/gradle/api/internal/artifacts/ivyservice/moduleconverter/DefaultModuleDescriptorFactoryTest.groovy
@@ -13,52 +13,28 @@
  * See the License for the specific language governing permissions and
  * limitations under the License.
  */
-package org.gradle.api.internal.artifacts.ivyservice.moduleconverter;
+package org.gradle.api.internal.artifacts.ivyservice.moduleconverter
 
-
-import org.apache.ivy.Ivy
 import org.apache.ivy.core.module.descriptor.DefaultModuleDescriptor
-import org.apache.ivy.core.module.id.ModuleId
-import org.apache.ivy.core.module.status.StatusManager
-import org.apache.ivy.core.settings.IvySettings
 import org.gradle.api.artifacts.Module
 import org.gradle.api.internal.artifacts.DefaultModule
-import org.gradle.api.internal.artifacts.ivyservice.IvyFactory
-import org.gradle.api.internal.artifacts.ivyservice.SettingsConverter
 import spock.lang.Specification
 
-/**
- * @author Hans Dockter
- */
 public class DefaultModuleDescriptorFactoryTest extends Specification {
-    final IvyFactory ivyFactory = Mock()
-    final SettingsConverter settingsConverter = Mock()
-    final DefaultModuleDescriptorFactory factory = new DefaultModuleDescriptorFactory(ivyFactory, settingsConverter)
+    final DefaultModuleDescriptorFactory factory = new DefaultModuleDescriptorFactory()
 
     public void testCreateModuleDescriptor() {
         given:
-        IvySettings ivySettings = Mock()
-        Ivy ivy = Mock()
-        StatusManager statusManager = Mock()
         Module module = new DefaultModule("org", "name", "version", "status");
 
         when:
         DefaultModuleDescriptor moduleDescriptor = factory.createModuleDescriptor(module);
 
         then:
-        1 * settingsConverter.getForResolve() >> ivySettings
-        1 * ivyFactory.createIvy(ivySettings) >> ivy
-        _ * ivy.settings >> ivySettings
-        1 * ivySettings.statusManager >> statusManager
-        1 * statusManager.defaultStatus >> "default status"
-        1 * ivySettings.getDefaultBranch(ModuleId.newInstance("org", "name")) >> "default branch"
-        0 * _._
-        
-        and:
-        moduleDescriptor.moduleRevisionId.organisation == module.group;
-        moduleDescriptor.moduleRevisionId.name == module.name;
-        moduleDescriptor.moduleRevisionId.revision == module.version;
-        moduleDescriptor.status == module.getStatus();
-        moduleDescriptor.publicationDate == null;
+        moduleDescriptor.moduleRevisionId.organisation == module.group
+        moduleDescriptor.moduleRevisionId.name == module.name
+        moduleDescriptor.moduleRevisionId.revision == module.version
+        moduleDescriptor.status == module.status
+        moduleDescriptor.publicationDate == null
     }
 }
diff --git a/subprojects/core-impl/src/test/groovy/org/gradle/api/internal/artifacts/ivyservice/moduleconverter/IvyConverterTestUtil.java b/subprojects/core-impl/src/test/groovy/org/gradle/api/internal/artifacts/ivyservice/moduleconverter/IvyConverterTestUtil.java
index 9cea636..3a06d68 100644
--- a/subprojects/core-impl/src/test/groovy/org/gradle/api/internal/artifacts/ivyservice/moduleconverter/IvyConverterTestUtil.java
+++ b/subprojects/core-impl/src/test/groovy/org/gradle/api/internal/artifacts/ivyservice/moduleconverter/IvyConverterTestUtil.java
@@ -19,9 +19,6 @@ import org.gradle.api.artifacts.Configuration;
 import org.jmock.Expectations;
 import org.jmock.Mockery;
 
-/**
- * @author Hans Dockter
- */
 public class IvyConverterTestUtil {
     public static Configuration createNamedConfigurationStub(final String name, Mockery context) {
         final Configuration configurationStub = context.mock(Configuration.class, name);
diff --git a/subprojects/core-impl/src/test/groovy/org/gradle/api/internal/artifacts/ivyservice/moduleconverter/PublishModuleDescriptorConverterTest.groovy b/subprojects/core-impl/src/test/groovy/org/gradle/api/internal/artifacts/ivyservice/moduleconverter/PublishModuleDescriptorConverterTest.groovy
index 1c0e2ff..33cacae 100644
--- a/subprojects/core-impl/src/test/groovy/org/gradle/api/internal/artifacts/ivyservice/moduleconverter/PublishModuleDescriptorConverterTest.groovy
+++ b/subprojects/core-impl/src/test/groovy/org/gradle/api/internal/artifacts/ivyservice/moduleconverter/PublishModuleDescriptorConverterTest.groovy
@@ -14,18 +14,15 @@
  * limitations under the License.
  */
 
-package org.gradle.api.internal.artifacts.ivyservice.moduleconverter;
-
+package org.gradle.api.internal.artifacts.ivyservice.moduleconverter
 
 import org.apache.ivy.core.module.descriptor.DefaultModuleDescriptor
 import org.gradle.api.artifacts.Configuration
 import org.gradle.api.artifacts.Module
+import org.gradle.api.internal.artifacts.BuildableModuleVersionPublishMetaData
 import org.gradle.api.internal.artifacts.ivyservice.ModuleDescriptorConverter
 import spock.lang.Specification
 
-/**
- * @author Hans Dockter, Szczepan
- */
 public class PublishModuleDescriptorConverterTest extends Specification {
 
     def "converts"() {
@@ -33,6 +30,7 @@ public class PublishModuleDescriptorConverterTest extends Specification {
         def configurationsDummy = [Mock(Configuration)] as Set
         def moduleDummy = Mock(Module)
         def moduleDescriptorDummy = Mock(DefaultModuleDescriptor)
+        def publishMetaDataDummy = Mock(BuildableModuleVersionPublishMetaData)
         def artifactsToModuleDescriptorConverter = Mock(ArtifactsToModuleDescriptorConverter)
         def resolveModuleDescriptorConverter = Mock(ModuleDescriptorConverter)
 
@@ -40,15 +38,19 @@ public class PublishModuleDescriptorConverterTest extends Specification {
                 resolveModuleDescriptorConverter,
                 artifactsToModuleDescriptorConverter);
 
-        resolveModuleDescriptorConverter.convert(configurationsDummy, moduleDummy) >> moduleDescriptorDummy
+        and:
+        publishMetaDataDummy.moduleDescriptor >> moduleDescriptorDummy
+        resolveModuleDescriptorConverter.convert(configurationsDummy, moduleDummy) >> publishMetaDataDummy
 
         when:
-        def actualModuleDescriptor = publishModuleDescriptorConverter.convert(configurationsDummy, moduleDummy);
+        def actualMetaData = publishModuleDescriptorConverter.convert(configurationsDummy, moduleDummy);
 
         then:
         1 * moduleDescriptorDummy.addExtraAttributeNamespace(PublishModuleDescriptorConverter.IVY_MAVEN_NAMESPACE_PREFIX,
                     PublishModuleDescriptorConverter.IVY_MAVEN_NAMESPACE);
-        1 * artifactsToModuleDescriptorConverter.addArtifacts(moduleDescriptorDummy, configurationsDummy)
-        actualModuleDescriptor == moduleDescriptorDummy
+        1 * artifactsToModuleDescriptorConverter.addArtifacts(publishMetaDataDummy, configurationsDummy)
+
+        and:
+        actualMetaData == publishMetaDataDummy
     }
 }
diff --git a/subprojects/core-impl/src/test/groovy/org/gradle/api/internal/artifacts/ivyservice/moduleconverter/ResolveModuleDescriptorConverterTest.groovy b/subprojects/core-impl/src/test/groovy/org/gradle/api/internal/artifacts/ivyservice/moduleconverter/ResolveModuleDescriptorConverterTest.groovy
index b28e9a1..31664ff 100644
--- a/subprojects/core-impl/src/test/groovy/org/gradle/api/internal/artifacts/ivyservice/moduleconverter/ResolveModuleDescriptorConverterTest.groovy
+++ b/subprojects/core-impl/src/test/groovy/org/gradle/api/internal/artifacts/ivyservice/moduleconverter/ResolveModuleDescriptorConverterTest.groovy
@@ -14,19 +14,16 @@
  * limitations under the License.
  */
 
-package org.gradle.api.internal.artifacts.ivyservice.moduleconverter;
-
+package org.gradle.api.internal.artifacts.ivyservice.moduleconverter
 
 import org.apache.ivy.core.module.descriptor.DefaultModuleDescriptor
+import org.apache.ivy.core.module.id.ModuleRevisionId
 import org.gradle.api.artifacts.Configuration
 import org.gradle.api.artifacts.Module
+import org.gradle.api.internal.artifacts.DefaultModuleVersionPublishMetaData
 import org.gradle.api.internal.artifacts.ivyservice.moduleconverter.dependencies.DependenciesToModuleDescriptorConverter
-import org.gradle.api.internal.artifacts.ivyservice.moduleconverter.dependencies.DependencyDescriptorFactory
 import spock.lang.Specification
 
-/**
- * @author Hans Dockter, Szczepan
- */
 public class ResolveModuleDescriptorConverterTest extends Specification {
 
     def "converts"() {
@@ -35,25 +32,27 @@ public class ResolveModuleDescriptorConverterTest extends Specification {
         def module = Mock(Module)
         def moduleDescriptor = Mock(DefaultModuleDescriptor)
         def moduleDescriptorFactory = Mock(ModuleDescriptorFactory)
-        def dependencyDescriptorFactory = Mock(DependencyDescriptorFactory)
         def configurationsConverter = Mock(ConfigurationsToModuleDescriptorConverter)
         def dependenciesConverter = Mock(DependenciesToModuleDescriptorConverter)
 
         ResolveModuleDescriptorConverter resolveModuleDescriptorConverter = new ResolveModuleDescriptorConverter(
                 moduleDescriptorFactory,
-                dependencyDescriptorFactory,
                 configurationsConverter,
                 dependenciesConverter);
 
-        moduleDescriptorFactory.createModuleDescriptor(module) >> moduleDescriptor
+        and:
+        moduleDescriptor.moduleRevisionId >> ModuleRevisionId.newInstance("group", "module", "version")
 
         when:
         def actualDescriptor = resolveModuleDescriptorConverter.convert(configurations, module);
 
         then:
+        1 * moduleDescriptorFactory.createModuleDescriptor(module) >> moduleDescriptor
         1 * configurationsConverter.addConfigurations(moduleDescriptor, configurations)
         1 * dependenciesConverter.addDependencyDescriptors(moduleDescriptor, configurations)
 
-        actualDescriptor == moduleDescriptor
+        and:
+        actualDescriptor instanceof DefaultModuleVersionPublishMetaData
+        actualDescriptor.moduleDescriptor == moduleDescriptor
     }
 }
diff --git a/subprojects/core-impl/src/test/groovy/org/gradle/api/internal/artifacts/ivyservice/moduleconverter/dependencies/AbstractDependencyDescriptorFactoryInternalTest.java b/subprojects/core-impl/src/test/groovy/org/gradle/api/internal/artifacts/ivyservice/moduleconverter/dependencies/AbstractDependencyDescriptorFactoryInternalTest.java
index 3d70134..2651112 100644
--- a/subprojects/core-impl/src/test/groovy/org/gradle/api/internal/artifacts/ivyservice/moduleconverter/dependencies/AbstractDependencyDescriptorFactoryInternalTest.java
+++ b/subprojects/core-impl/src/test/groovy/org/gradle/api/internal/artifacts/ivyservice/moduleconverter/dependencies/AbstractDependencyDescriptorFactoryInternalTest.java
@@ -30,7 +30,7 @@ import org.gradle.api.artifacts.ExcludeRule;
 import org.gradle.api.artifacts.ModuleDependency;
 import org.gradle.api.internal.artifacts.dependencies.DefaultDependencyArtifact;
 import org.gradle.api.internal.artifacts.ivyservice.moduleconverter.ExcludeRuleConverter;
-import org.gradle.util.HelperUtil;
+import org.gradle.util.TestUtil;
 import org.gradle.util.WrapUtil;
 import org.jmock.Expectations;
 import org.jmock.integration.junit4.JMock;
@@ -47,9 +47,6 @@ import static org.hamcrest.Matchers.equalTo;
 import static org.junit.Assert.assertEquals;
 import static org.junit.Assert.assertThat;
 
-/**
- * @author Hans Dockter
- */
 @RunWith(JMock.class)
 public abstract class AbstractDependencyDescriptorFactoryInternalTest {
     private JUnit4Mockery context = new JUnit4Mockery();
@@ -60,7 +57,7 @@ public abstract class AbstractDependencyDescriptorFactoryInternalTest {
     protected static final ExcludeRule TEST_EXCLUDE_RULE = new org.gradle.api.internal.artifacts.DefaultExcludeRule("testOrg", null);
     protected static final org.apache.ivy.core.module.descriptor.ExcludeRule TEST_IVY_EXCLUDE_RULE = getTestExcludeRule();
     protected ExcludeRuleConverter excludeRuleConverterStub = context.mock(ExcludeRuleConverter.class);
-    protected final DefaultModuleDescriptor moduleDescriptor = HelperUtil.createModuleDescriptor(WrapUtil.toSet(TEST_CONF));
+    protected final DefaultModuleDescriptor moduleDescriptor = TestUtil.createModuleDescriptor(WrapUtil.toSet(TEST_CONF));
     private DefaultDependencyArtifact artifact = new DefaultDependencyArtifact("name", "type", null, null, null);
     private DefaultDependencyArtifact artifactWithClassifiers = new DefaultDependencyArtifact("name2", "type2", "ext2", "classifier2", "http://www.url2.com");
 
diff --git a/subprojects/core-impl/src/test/groovy/org/gradle/api/internal/artifacts/ivyservice/moduleconverter/dependencies/ClientModuleDependencyDescriptorFactoryTest.java b/subprojects/core-impl/src/test/groovy/org/gradle/api/internal/artifacts/ivyservice/moduleconverter/dependencies/ClientModuleDependencyDescriptorFactoryTest.java
index 6f21ece..158cace 100644
--- a/subprojects/core-impl/src/test/groovy/org/gradle/api/internal/artifacts/ivyservice/moduleconverter/dependencies/ClientModuleDependencyDescriptorFactoryTest.java
+++ b/subprojects/core-impl/src/test/groovy/org/gradle/api/internal/artifacts/ivyservice/moduleconverter/dependencies/ClientModuleDependencyDescriptorFactoryTest.java
@@ -32,9 +32,6 @@ import org.junit.Test;
 import static org.hamcrest.Matchers.equalTo;
 import static org.junit.Assert.*;
 
-/**
- * @author Hans Dockter
- */
 public class ClientModuleDependencyDescriptorFactoryTest extends AbstractDependencyDescriptorFactoryInternalTest {
     private JUnit4Mockery context = new JUnit4Mockery();
 
diff --git a/subprojects/core-impl/src/test/groovy/org/gradle/api/internal/artifacts/ivyservice/moduleconverter/dependencies/DefaultDependenciesToModuleDescriptorConverterTest.java b/subprojects/core-impl/src/test/groovy/org/gradle/api/internal/artifacts/ivyservice/moduleconverter/dependencies/DefaultDependenciesToModuleDescriptorConverterTest.java
index 12f2ca1..1ac36a0 100644
--- a/subprojects/core-impl/src/test/groovy/org/gradle/api/internal/artifacts/ivyservice/moduleconverter/dependencies/DefaultDependenciesToModuleDescriptorConverterTest.java
+++ b/subprojects/core-impl/src/test/groovy/org/gradle/api/internal/artifacts/ivyservice/moduleconverter/dependencies/DefaultDependenciesToModuleDescriptorConverterTest.java
@@ -24,7 +24,7 @@ import org.gradle.api.internal.artifacts.DefaultExcludeRule;
 import org.gradle.api.internal.artifacts.dependencies.DefaultExternalModuleDependency;
 import org.gradle.api.internal.artifacts.ivyservice.moduleconverter.ExcludeRuleConverter;
 import org.gradle.api.internal.artifacts.ivyservice.moduleconverter.IvyConverterTestUtil;
-import org.gradle.util.HelperUtil;
+import org.gradle.util.TestUtil;
 import org.gradle.util.JUnit4GroovyMockery;
 import org.gradle.util.WrapUtil;
 import org.jmock.Expectations;
@@ -40,9 +40,6 @@ import static org.hamcrest.Matchers.equalTo;
 import static org.hamcrest.Matchers.sameInstance;
 import static org.junit.Assert.assertThat;
 
-/**
- * @author Hans Dockter
- */
 @RunWith(JMock.class)
 public class DefaultDependenciesToModuleDescriptorConverterTest {
     private JUnit4Mockery context = new JUnit4GroovyMockery();
@@ -68,7 +65,7 @@ public class DefaultDependenciesToModuleDescriptorConverterTest {
         Configuration configurationStub1 = createNamedConfigurationStubWithDependenciesAndExcludeRules("conf1", GRADLE_EXCLUDE_RULE_DUMMY_1, dependencyDummy1, similarDependency1);
         Configuration configurationStub2 = createNamedConfigurationStubWithDependenciesAndExcludeRules("conf2", GRADLE_EXCLUDE_RULE_DUMMY_2, dependencyDummy2, similarDependency2);
         Configuration configurationStub3 = createNamedConfigurationStubWithDependenciesAndExcludeRules("conf3", null, similarDependency3);
-        final DefaultModuleDescriptor moduleDescriptor = HelperUtil.createModuleDescriptor(toSet(configurationStub1.getName(),
+        final DefaultModuleDescriptor moduleDescriptor = TestUtil.createModuleDescriptor(toSet(configurationStub1.getName(),
                 configurationStub2.getName()));
         associateDependencyWithDescriptor(dependencyDummy1, moduleDescriptor, configurationStub1);
         associateDependencyWithDescriptor(dependencyDummy2, moduleDescriptor, configurationStub2);
diff --git a/subprojects/core-impl/src/test/groovy/org/gradle/api/internal/artifacts/ivyservice/moduleconverter/dependencies/DefaultModuleDescriptorFactoryForClientModuleTest.java b/subprojects/core-impl/src/test/groovy/org/gradle/api/internal/artifacts/ivyservice/moduleconverter/dependencies/DefaultModuleDescriptorFactoryForClientModuleTest.java
index bfa2b1c..330109d 100644
--- a/subprojects/core-impl/src/test/groovy/org/gradle/api/internal/artifacts/ivyservice/moduleconverter/dependencies/DefaultModuleDescriptorFactoryForClientModuleTest.java
+++ b/subprojects/core-impl/src/test/groovy/org/gradle/api/internal/artifacts/ivyservice/moduleconverter/dependencies/DefaultModuleDescriptorFactoryForClientModuleTest.java
@@ -28,9 +28,6 @@ import org.junit.Test;
 import static org.hamcrest.Matchers.equalTo;
 import static org.junit.Assert.assertThat;
 
-/**
- * @author Hans Dockter
- */
 public class DefaultModuleDescriptorFactoryForClientModuleTest {
     private JUnit4Mockery context = new JUnit4Mockery();
 
diff --git a/subprojects/core-impl/src/test/groovy/org/gradle/api/internal/artifacts/ivyservice/moduleconverter/dependencies/ExternalModuleDependencyDescriptorFactoryTest.java b/subprojects/core-impl/src/test/groovy/org/gradle/api/internal/artifacts/ivyservice/moduleconverter/dependencies/ExternalModuleDependencyDescriptorFactoryTest.java
index 1b58c4c..7cd5814 100644
--- a/subprojects/core-impl/src/test/groovy/org/gradle/api/internal/artifacts/ivyservice/moduleconverter/dependencies/ExternalModuleDependencyDescriptorFactoryTest.java
+++ b/subprojects/core-impl/src/test/groovy/org/gradle/api/internal/artifacts/ivyservice/moduleconverter/dependencies/ExternalModuleDependencyDescriptorFactoryTest.java
@@ -30,9 +30,6 @@ import static org.hamcrest.Matchers.equalTo;
 import static org.junit.Assert.assertEquals;
 import static org.junit.Assert.assertThat;
 
-/**
- * @author Hans Dockter
- */
 public class ExternalModuleDependencyDescriptorFactoryTest extends AbstractDependencyDescriptorFactoryInternalTest {
     private JUnit4Mockery context = new JUnit4Mockery();
 
diff --git a/subprojects/core-impl/src/test/groovy/org/gradle/api/internal/artifacts/ivyservice/moduleconverter/dependencies/ProjectDependencyDescriptorFactoryTest.groovy b/subprojects/core-impl/src/test/groovy/org/gradle/api/internal/artifacts/ivyservice/moduleconverter/dependencies/ProjectDependencyDescriptorFactoryTest.groovy
new file mode 100644
index 0000000..067651c
--- /dev/null
+++ b/subprojects/core-impl/src/test/groovy/org/gradle/api/internal/artifacts/ivyservice/moduleconverter/dependencies/ProjectDependencyDescriptorFactoryTest.groovy
@@ -0,0 +1,64 @@
+/*
+ * Copyright 2010 the original author or authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.gradle.api.internal.artifacts.ivyservice.moduleconverter.dependencies;
+
+
+import org.apache.ivy.core.module.id.ModuleRevisionId
+import org.gradle.api.artifacts.ExternalModuleDependency
+import org.gradle.api.artifacts.ProjectDependency
+import org.gradle.api.internal.artifacts.dependencies.DefaultProjectDependency
+import org.gradle.api.internal.project.AbstractProject
+import org.gradle.initialization.ProjectAccessListener
+import org.gradle.util.TestUtil
+import org.jmock.integration.junit4.JUnit4Mockery
+import org.junit.Test
+
+import static org.hamcrest.Matchers.equalTo
+import static org.junit.Assert.*
+
+//TODO SF spockify
+public class ProjectDependencyDescriptorFactoryTest extends AbstractDependencyDescriptorFactoryInternalTest {
+    private JUnit4Mockery context = new JUnit4Mockery();
+
+    private ProjectIvyDependencyDescriptorFactory projectDependencyDescriptorFactory =
+            new ProjectIvyDependencyDescriptorFactory(excludeRuleConverterStub);
+
+    @Test
+    public void canConvert() {
+        assertThat(projectDependencyDescriptorFactory.canConvert(context.mock(ProjectDependency.class)), equalTo(true));
+        assertThat(projectDependencyDescriptorFactory.canConvert(context.mock(ExternalModuleDependency.class)), equalTo(false));
+    }
+
+    @Test
+    public void testCreateFromProjectDependency() {
+        ProjectDependency projectDependency = createProjectDependency(TEST_DEP_CONF);
+        setUpDependency(projectDependency);
+        ProjectDependencyDescriptor dependencyDescriptor = (ProjectDependencyDescriptor) projectDependencyDescriptorFactory.createDependencyDescriptor(TEST_CONF, projectDependency, moduleDescriptor);
+
+        assertDependencyDescriptorHasCommonFixtureValues(dependencyDescriptor);
+        assertFalse(dependencyDescriptor.isChanging());
+        assertFalse(dependencyDescriptor.isForce());
+        assertEquals(ModuleRevisionId.newInstance("someGroup", "test", "someVersion"), dependencyDescriptor.getDependencyRevisionId());
+        assertSame(projectDependency.getDependencyProject(), dependencyDescriptor.getTargetProject());
+    }
+
+    private ProjectDependency createProjectDependency(String dependencyConfiguration) {
+        AbstractProject dependencyProject = TestUtil.createRootProject();
+        dependencyProject.setGroup("someGroup");
+        dependencyProject.setVersion("someVersion");
+        return new DefaultProjectDependency(dependencyProject, dependencyConfiguration, {} as ProjectAccessListener, true);
+    }
+}
\ No newline at end of file
diff --git a/subprojects/core-impl/src/test/groovy/org/gradle/api/internal/artifacts/ivyservice/moduleconverter/dependencies/ProjectDependencyDescriptorFactoryTest.java b/subprojects/core-impl/src/test/groovy/org/gradle/api/internal/artifacts/ivyservice/moduleconverter/dependencies/ProjectDependencyDescriptorFactoryTest.java
deleted file mode 100644
index e5b7fb9..0000000
--- a/subprojects/core-impl/src/test/groovy/org/gradle/api/internal/artifacts/ivyservice/moduleconverter/dependencies/ProjectDependencyDescriptorFactoryTest.java
+++ /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.artifacts.ivyservice.moduleconverter.dependencies;
-
-import org.apache.ivy.core.module.id.ModuleRevisionId;
-import org.gradle.api.artifacts.ExternalModuleDependency;
-import org.gradle.api.artifacts.ProjectDependency;
-import org.gradle.api.internal.artifacts.dependencies.DefaultProjectDependency;
-import org.gradle.api.internal.project.AbstractProject;
-import org.gradle.util.HelperUtil;
-import org.jmock.integration.junit4.JUnit4Mockery;
-import org.junit.Test;
-
-import static org.hamcrest.Matchers.equalTo;
-import static org.junit.Assert.*;
-
-/**
- * @author Hans Dockter
- */
-//TODO SF spockify
-public class ProjectDependencyDescriptorFactoryTest extends AbstractDependencyDescriptorFactoryInternalTest {
-    private JUnit4Mockery context = new JUnit4Mockery();
-
-    private ProjectIvyDependencyDescriptorFactory projectDependencyDescriptorFactory =
-            new ProjectIvyDependencyDescriptorFactory(excludeRuleConverterStub);
-
-    @Test
-    public void canConvert() {
-        assertThat(projectDependencyDescriptorFactory.canConvert(context.mock(ProjectDependency.class)), equalTo(true));
-        assertThat(projectDependencyDescriptorFactory.canConvert(context.mock(ExternalModuleDependency.class)), equalTo(false));
-    }
-
-    @Test
-    public void testCreateFromProjectDependency() {
-        ProjectDependency projectDependency = createProjectDependency(TEST_DEP_CONF);
-        setUpDependency(projectDependency);
-        ProjectDependencyDescriptor dependencyDescriptor = (ProjectDependencyDescriptor) projectDependencyDescriptorFactory.createDependencyDescriptor(TEST_CONF, projectDependency, moduleDescriptor);
-
-        assertDependencyDescriptorHasCommonFixtureValues(dependencyDescriptor);
-        assertFalse(dependencyDescriptor.isChanging());
-        assertFalse(dependencyDescriptor.isForce());
-        assertEquals(ModuleRevisionId.newInstance("someGroup", "test", "someVersion"), dependencyDescriptor.getDependencyRevisionId());
-        assertSame(projectDependency.getDependencyProject(), dependencyDescriptor.getTargetProject());
-    }
-
-    private ProjectDependency createProjectDependency(String dependencyConfiguration) {
-        AbstractProject dependencyProject = HelperUtil.createRootProject();
-        dependencyProject.setGroup("someGroup");
-        dependencyProject.setVersion("someVersion");
-        return new DefaultProjectDependency(dependencyProject, dependencyConfiguration, null, true);
-    }
-}
\ No newline at end of file
diff --git a/subprojects/core-impl/src/test/groovy/org/gradle/api/internal/artifacts/ivyservice/moduleconverter/dependencies/ReflectiveDependencyDescriptorFactoryTest.groovy b/subprojects/core-impl/src/test/groovy/org/gradle/api/internal/artifacts/ivyservice/moduleconverter/dependencies/ReflectiveDependencyDescriptorFactoryTest.groovy
index 9b92d9b..294e868 100644
--- a/subprojects/core-impl/src/test/groovy/org/gradle/api/internal/artifacts/ivyservice/moduleconverter/dependencies/ReflectiveDependencyDescriptorFactoryTest.groovy
+++ b/subprojects/core-impl/src/test/groovy/org/gradle/api/internal/artifacts/ivyservice/moduleconverter/dependencies/ReflectiveDependencyDescriptorFactoryTest.groovy
@@ -16,13 +16,10 @@
 
 package org.gradle.api.internal.artifacts.ivyservice.moduleconverter.dependencies
 
+import org.apache.ivy.core.module.descriptor.*
 import org.apache.ivy.core.module.id.ModuleRevisionId
 import spock.lang.Specification
-import org.apache.ivy.core.module.descriptor.*
 
-/**
- * by Szczepan Faber, created at: 1/29/13
- */
 class ReflectiveDependencyDescriptorFactoryTest extends Specification {
 
     def factory = new ReflectiveDependencyDescriptorFactory()
diff --git a/subprojects/core-impl/src/test/groovy/org/gradle/api/internal/artifacts/ivyservice/projectmodule/ProjectDependencyResolverTest.groovy b/subprojects/core-impl/src/test/groovy/org/gradle/api/internal/artifacts/ivyservice/projectmodule/ProjectDependencyResolverTest.groovy
index 30d2d65..32a3db8 100644
--- a/subprojects/core-impl/src/test/groovy/org/gradle/api/internal/artifacts/ivyservice/projectmodule/ProjectDependencyResolverTest.groovy
+++ b/subprojects/core-impl/src/test/groovy/org/gradle/api/internal/artifacts/ivyservice/projectmodule/ProjectDependencyResolverTest.groovy
@@ -17,32 +17,32 @@ package org.gradle.api.internal.artifacts.ivyservice.projectmodule
 
 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.gradle.api.artifacts.ModuleVersionIdentifier
+import org.gradle.api.internal.artifacts.ModuleVersionPublishMetaData
 import org.gradle.api.internal.artifacts.ivyservice.BuildableModuleVersionResolveResult
-import org.gradle.api.internal.artifacts.ivyservice.DependencyToModuleResolver
+import org.gradle.api.internal.artifacts.ivyservice.DependencyToModuleVersionResolver
+import org.gradle.api.internal.artifacts.ivyservice.ModuleDescriptorConverter
 import org.gradle.api.internal.artifacts.ivyservice.ivyresolve.DependencyMetaData
 import org.gradle.api.internal.artifacts.ivyservice.moduleconverter.dependencies.ProjectDependencyDescriptor
 import org.gradle.api.internal.project.ProjectInternal
-import org.gradle.initialization.ProjectAccessListener
 import spock.lang.Specification
 
 class ProjectDependencyResolverTest extends Specification {
     final ProjectModuleRegistry registry = Mock()
-    final ModuleRevisionId moduleRevisionId = Mock()
-    final DependencyToModuleResolver target = Mock()
-    final ProjectAccessListener projectAccessListener = Mock()
-    final ProjectDependencyResolver resolver = new ProjectDependencyResolver(registry, target, projectAccessListener)
+    final DependencyToModuleVersionResolver target = Mock()
+    final ModuleDescriptorConverter converter = Mock()
+    final ProjectDependencyResolver resolver = new ProjectDependencyResolver(registry, target, converter)
 
     def "resolves project dependency"() {
         setup:
-        1 * moduleRevisionId.organisation >> "group"
-        1 * moduleRevisionId.name >> "project"
-        1 * moduleRevisionId.revision >> "1.0"
-
-        def moduleDescriptor = Mock(ModuleDescriptor)
+        def ModuleVersionIdentifier moduleId = Stub(ModuleVersionIdentifier)
+        def moduleDescriptor = Stub(ModuleDescriptor)
+        def publishMetaData = Stub(ModuleVersionPublishMetaData) {
+            getId() >> moduleId
+            getModuleDescriptor() >> moduleDescriptor
+        }
         def result = Mock(BuildableModuleVersionResolveResult)
-        def dependencyProject = Mock(ProjectInternal)
+        def dependencyProject = Stub(ProjectInternal)
         def dependencyDescriptor = Stub(ProjectDependencyDescriptor) {
             getTargetProject() >> dependencyProject
         }
@@ -54,21 +54,14 @@ class ProjectDependencyResolverTest extends Specification {
         resolver.resolve(dependencyMetaData, result)
 
         then:
-        1 * registry.findProject(dependencyDescriptor) >> moduleDescriptor
-        _ * moduleDescriptor.moduleRevisionId >> moduleRevisionId
-        1 * result.resolved(_, moduleDescriptor, _) >> { args ->
-            ModuleVersionIdentifier moduleVersionIdentifier = args[0]
-            moduleVersionIdentifier.group == "group"
-            moduleVersionIdentifier.name == "project"
-            moduleVersionIdentifier.version == "1.0"
-        }
-        1 * projectAccessListener.beforeResolvingProjectDependency(dependencyProject)
+        1 * registry.findProject(dependencyDescriptor) >> publishMetaData
+        1 * result.resolved(moduleId, moduleDescriptor, _)
         0 * result._
     }
 
     def "delegates to backing resolver for non-project dependency"() {
         def result = Mock(BuildableModuleVersionResolveResult)
-        def dependencyDescriptor = Mock(DependencyDescriptor)
+        def dependencyDescriptor = Stub(DependencyDescriptor)
         def dependencyMetaData = Stub(DependencyMetaData) {
             getDescriptor() >> dependencyDescriptor
         }
diff --git a/subprojects/core-impl/src/test/groovy/org/gradle/api/internal/artifacts/ivyservice/resolutionstrategy/DefaultResolutionStrategySpec.groovy b/subprojects/core-impl/src/test/groovy/org/gradle/api/internal/artifacts/ivyservice/resolutionstrategy/DefaultResolutionStrategySpec.groovy
index 0d8a100..577f4eb 100644
--- a/subprojects/core-impl/src/test/groovy/org/gradle/api/internal/artifacts/ivyservice/resolutionstrategy/DefaultResolutionStrategySpec.groovy
+++ b/subprojects/core-impl/src/test/groovy/org/gradle/api/internal/artifacts/ivyservice/resolutionstrategy/DefaultResolutionStrategySpec.groovy
@@ -27,9 +27,6 @@ import java.util.concurrent.TimeUnit
 import static org.gradle.api.internal.artifacts.DefaultModuleVersionSelector.newSelector
 import static org.gradle.util.Assertions.assertThat
 
-/**
- * by Szczepan Faber, created at: 11/2/11
- */
 public class DefaultResolutionStrategySpec extends Specification {
 
     def cachePolicy = Mock(DefaultCachePolicy)
diff --git a/subprojects/core-impl/src/test/groovy/org/gradle/api/internal/artifacts/ivyservice/resolutionstrategy/ModuleForcingResolveRuleSpec.groovy b/subprojects/core-impl/src/test/groovy/org/gradle/api/internal/artifacts/ivyservice/resolutionstrategy/ModuleForcingResolveRuleSpec.groovy
index 29b75eb..e806a87 100644
--- a/subprojects/core-impl/src/test/groovy/org/gradle/api/internal/artifacts/ivyservice/resolutionstrategy/ModuleForcingResolveRuleSpec.groovy
+++ b/subprojects/core-impl/src/test/groovy/org/gradle/api/internal/artifacts/ivyservice/resolutionstrategy/ModuleForcingResolveRuleSpec.groovy
@@ -22,9 +22,6 @@ import spock.lang.Specification
 
 import static org.gradle.api.internal.artifacts.DefaultModuleVersionSelector.newSelector
 
-/**
- * by Szczepan Faber, created at: 11/29/12
- */
 class ModuleForcingResolveRuleSpec extends Specification {
 
     def "forces modules"() {
diff --git a/subprojects/core-impl/src/test/groovy/org/gradle/api/internal/artifacts/ivyservice/resolveengine/DependencyGraphBuilderTest.groovy b/subprojects/core-impl/src/test/groovy/org/gradle/api/internal/artifacts/ivyservice/resolveengine/DependencyGraphBuilderTest.groovy
index 48e159f..b66937f 100644
--- a/subprojects/core-impl/src/test/groovy/org/gradle/api/internal/artifacts/ivyservice/resolveengine/DependencyGraphBuilderTest.groovy
+++ b/subprojects/core-impl/src/test/groovy/org/gradle/api/internal/artifacts/ivyservice/resolveengine/DependencyGraphBuilderTest.groovy
@@ -19,46 +19,42 @@ import org.apache.ivy.core.module.descriptor.*
 import org.apache.ivy.core.module.id.ArtifactId
 import org.apache.ivy.core.module.id.ModuleId
 import org.apache.ivy.core.module.id.ModuleRevisionId
-import org.apache.ivy.core.resolve.ResolveData
-import org.apache.ivy.core.resolve.ResolveEngine
-import org.apache.ivy.core.resolve.ResolveOptions
 import org.apache.ivy.plugins.matcher.ExactPatternMatcher
 import org.apache.ivy.plugins.matcher.PatternMatcher
 import org.gradle.api.artifacts.*
 import org.gradle.api.internal.artifacts.DefaultModuleVersionSelector
-import org.gradle.api.internal.artifacts.DefaultResolvedArtifact
 import org.gradle.api.internal.artifacts.configurations.ConfigurationInternal
 import org.gradle.api.internal.artifacts.ivyservice.*
-import org.gradle.api.internal.artifacts.ivyservice.ivyresolve.DefaultBuildableModuleVersionMetaData
+import org.gradle.api.internal.artifacts.ivyservice.ivyresolve.DefaultBuildableModuleVersionMetaDataResolveResult
 import org.gradle.api.internal.artifacts.ivyservice.ivyresolve.ModuleVersionMetaData
 import org.gradle.api.internal.artifacts.ivyservice.moduleconverter.dependencies.EnhancedDependencyDescriptor
+import org.gradle.api.internal.artifacts.ivyservice.resolveengine.oldresult.DefaultResolvedConfigurationBuilder
+import org.gradle.api.internal.artifacts.ivyservice.resolveengine.oldresult.TransientResultsStore
+import org.gradle.api.internal.artifacts.ivyservice.resolveengine.result.DummyBinaryStore
+import org.gradle.api.internal.artifacts.ivyservice.resolveengine.result.DummyStore
 import org.gradle.api.internal.artifacts.ivyservice.resolveengine.result.ResolvedConfigurationListener
+import org.gradle.api.internal.artifacts.ivyservice.resolveengine.result.VersionSelectionReasons
 import org.gradle.api.specs.Spec
 import spock.lang.Specification
 
 import static org.gradle.api.internal.artifacts.DefaultModuleVersionIdentifier.newId
 import static org.gradle.api.internal.artifacts.DefaultModuleVersionSelector.newSelector
-import org.gradle.api.internal.artifacts.ivyservice.resolveengine.result.VersionSelectionReasons
 
 class DependencyGraphBuilderTest extends Specification {
-    final ModuleDescriptorConverter moduleDescriptorConverter = Mock()
-    final ResolvedArtifactFactory resolvedArtifactFactory = Mock()
     final ConfigurationInternal configuration = Mock()
-    final ResolveEngine resolveEngine = Mock()
-    final ResolveData resolveData = new ResolveData(resolveEngine, new ResolveOptions())
     final ModuleConflictResolver conflictResolver = Mock()
     final DependencyToModuleVersionIdResolver dependencyResolver = Mock()
     final ResolvedConfigurationListener listener = Mock()
     final ModuleVersionMetaData root = revision('root')
-    final DependencyGraphBuilder builder = new DependencyGraphBuilder(moduleDescriptorConverter, resolvedArtifactFactory, dependencyResolver, conflictResolver)
+    final ModuleToModuleVersionResolver moduleResolver = Mock()
+    final DependencyToConfigurationResolver dependencyToConfigurationResolver = new DefaultDependencyToConfigurationResolver()
+    final DependencyGraphBuilder builder = new DependencyGraphBuilder(dependencyResolver, moduleResolver, conflictResolver, dependencyToConfigurationResolver)
 
     def setup() {
         config(root, 'root', 'default')
         _ * configuration.name >> 'root'
-        _ * moduleDescriptorConverter.convert(_, _) >> root.descriptor
-        _ * resolvedArtifactFactory.create(_, _, _) >> { owner, artifact, resolver ->
-            return new DefaultResolvedArtifact(owner, artifact, null)
-        }
+        _ * configuration.path >> 'root'
+        _ * moduleResolver.resolve(_, _, _) >> { it[2].resolved(root, Mock(ArtifactResolver)) }
     }
 
     def "does not resolve a given module selector more than once"() {
@@ -72,13 +68,20 @@ class DependencyGraphBuilderTest extends Specification {
         doesNotResolve b, c
 
         when:
-        def result = builder.resolve(configuration, resolveData, listener)
+        def result = resolve()
         result.rethrowFailure()
 
         then:
         modules(result) == ids(a, b, c)
     }
 
+    private DefaultLenientConfiguration resolve() {
+        def results = new DefaultResolvedConfigurationBuilder(Stub(ResolvedArtifactFactory),
+                new TransientResultsStore(new DummyBinaryStore(), new DummyStore()))
+        builder.resolve(configuration, listener, results)
+        new DefaultLenientConfiguration(configuration, results, Stub(CacheLockingManager))
+    }
+
     def "correctly notifies the resolved configuration listener"() {
         given:
         def a = revision("a")
@@ -91,7 +94,7 @@ class DependencyGraphBuilderTest extends Specification {
         traversesMissing a, d
 
         when:
-        builder.resolve(configuration, resolveData, listener)
+        resolve()
 
         then:
         1 * listener.start(newId("group", "root", "1.0"))
@@ -115,7 +118,7 @@ class DependencyGraphBuilderTest extends Specification {
         doesNotResolve c, d
 
         when:
-        def result = builder.resolve(configuration, resolveData, listener)
+        def result = resolve()
         result.rethrowFailure()
 
         then:
@@ -138,13 +141,14 @@ class DependencyGraphBuilderTest extends Specification {
         doesNotResolve evicted, e
 
         when:
-        def result = builder.resolve(configuration, resolveData, listener)
+        def result = resolve()
         result.rethrowFailure()
 
         then:
-        1 * conflictResolver.select(!null, !null) >> { Collection<ModuleRevisionResolveState> candidates, ModuleRevisionResolveState root ->
-            assert candidates*.revision == ['1.2', '1.1']
-            return candidates.find { it.revision == '1.2' }
+        1 * conflictResolver.select(!null) >> {
+            Collection<ModuleRevisionResolveState> candidates = it[0]
+            assert candidates*.version == ['1.2', '1.1']
+            return candidates.find { it.version == '1.2' }
         }
         0 * conflictResolver._
 
@@ -168,13 +172,14 @@ class DependencyGraphBuilderTest extends Specification {
         traverses selected, e
 
         when:
-        def result = builder.resolve(configuration, resolveData, listener)
+        def result = resolve()
         result.rethrowFailure()
 
         then:
-        1 * conflictResolver.select(!null, !null) >> { Collection<ModuleRevisionResolveState> candidates, ModuleRevisionResolveState root ->
-            assert candidates*.revision == ['1.1', '1.2']
-            return candidates.find { it.revision == '1.2' }
+        1 * conflictResolver.select(!null) >> {
+            Collection<ModuleRevisionResolveState> candidates = it[0]
+            assert candidates*.version == ['1.1', '1.2']
+            return candidates.find { it.version == '1.2' }
         }
         0 * conflictResolver._
 
@@ -198,13 +203,14 @@ class DependencyGraphBuilderTest extends Specification {
         traverses selected, e
 
         when:
-        def result = builder.resolve(configuration, resolveData, listener)
+        def result = resolve()
         result.rethrowFailure()
 
         then:
-        1 * conflictResolver.select(!null, !null) >> { Collection<ModuleRevisionResolveState> candidates, ModuleRevisionResolveState root ->
-            assert candidates*.revision == ['1.1', '1.2']
-            return candidates.find { it.revision == '1.2' }
+        1 * conflictResolver.select(!null) >> {
+            Collection<ModuleRevisionResolveState> candidates = it[0]
+            assert candidates*.version == ['1.1', '1.2']
+            return candidates.find { it.version == '1.2' }
         }
         0 * conflictResolver._
 
@@ -224,13 +230,14 @@ class DependencyGraphBuilderTest extends Specification {
         doesNotResolve evicted, c
 
         when:
-        def result = builder.resolve(configuration, resolveData, listener)
+        def result = resolve()
         result.rethrowFailure()
 
         then:
-        1 * conflictResolver.select(!null, !null) >> { Collection<ModuleRevisionResolveState> candidates, ModuleRevisionResolveState root ->
-            assert candidates*.revision == ['1.2', '1.1']
-            return candidates.find { it.revision == '1.2' }
+        1 * conflictResolver.select(!null) >> {
+            Collection<ModuleRevisionResolveState> candidates = it[0]
+            assert candidates*.version == ['1.2', '1.1']
+            return candidates.find { it.version == '1.2' }
         }
         0 * conflictResolver._
 
@@ -253,13 +260,14 @@ class DependencyGraphBuilderTest extends Specification {
         doesNotResolve b, evicted
 
         when:
-        def result = builder.resolve(configuration, resolveData, listener)
+        def result = resolve()
         result.rethrowFailure()
 
         then:
-        1 * conflictResolver.select(!null, !null) >> { Collection<ModuleRevisionResolveState> candidates, ModuleRevisionResolveState root ->
-            assert candidates*.revision == ['1.1', '1.2']
-            return candidates.find { it.revision == '1.2' }
+        1 * conflictResolver.select(!null) >> {
+            Collection<ModuleRevisionResolveState> candidates = it[0]
+            assert candidates*.version == ['1.1', '1.2']
+            return candidates.find { it.version == '1.2' }
         }
         0 * conflictResolver._
 
@@ -283,18 +291,21 @@ class DependencyGraphBuilderTest extends Specification {
         doesNotResolve selectedB, evictedA2
 
         when:
-        def result = builder.resolve(configuration, resolveData, listener)
+        def result = resolve()
         result.rethrowFailure()
 
         then:
-        1 * conflictResolver.select({ it*.revision == ['1.1', '1.2'] }, !null) >> { Collection<ModuleRevisionResolveState> candidates, ModuleRevisionResolveState root ->
-            return candidates.find { it.revision == '1.2' }
+        1 * conflictResolver.select({ it*.version == ['1.1', '1.2'] }) >> {
+            Collection<ModuleRevisionResolveState> candidates = it[0]
+            return candidates.find { it.version == '1.2' }
         }
-        1 * conflictResolver.select({ it*.revision == ['2.1', '2.2'] }, !null) >> { Collection<ModuleRevisionResolveState> candidates, ModuleRevisionResolveState root ->
-            return candidates.find { it.revision == '2.2' }
+        1 * conflictResolver.select({ it*.version == ['2.1', '2.2'] }) >> {
+            Collection<ModuleRevisionResolveState> candidates = it[0]
+            return candidates.find { it.version == '2.2' }
         }
-        1 * conflictResolver.select({ it*.revision == ['1.1', '1.2', '1.0'] }, !null) >> { Collection<ModuleRevisionResolveState> candidates, ModuleRevisionResolveState root ->
-            return candidates.find { it.revision == '1.2' }
+        1 * conflictResolver.select({ it*.version == ['1.1', '1.2', '1.0'] }) >> {
+            Collection<ModuleRevisionResolveState> candidates = it[0]
+            return candidates.find { it.version == '1.2' }
         }
         0 * conflictResolver._
 
@@ -319,12 +330,13 @@ class DependencyGraphBuilderTest extends Specification {
         traverses e, selected // conflict is deeper than 'b', to ensure 'b' has been visited
 
         when:
-        def result = builder.resolve(configuration, resolveData, listener)
+        def result = resolve()
         result.rethrowFailure()
 
         then:
-        1 * conflictResolver.select({ it*.revision == ['1', '2'] }, !null) >> { Collection<ModuleRevisionResolveState> candidates, ModuleRevisionResolveState root ->
-            return candidates.find { it.revision == '2' }
+        1 * conflictResolver.select({ it*.version == ['1', '2'] }) >> {
+            Collection<ModuleRevisionResolveState> candidates = it[0]
+            return candidates.find { it.version == '2' }
         }
         0 * conflictResolver._
 
@@ -349,12 +361,13 @@ class DependencyGraphBuilderTest extends Specification {
         traverses e, selected // conflict is deeper than 'b', to ensure 'b' has been visited
 
         when:
-        def result = builder.resolve(configuration, resolveData, listener)
+        def result = resolve()
         result.rethrowFailure()
 
         then:
-        1 * conflictResolver.select({ it*.revision == ['1', '2'] }, !null) >> { Collection<ModuleRevisionResolveState> candidates, ModuleRevisionResolveState root ->
-            return candidates.find { it.revision == '2' }
+        1 * conflictResolver.select({ it*.version == ['1', '2'] }) >> {
+            Collection<ModuleRevisionResolveState> candidates = it[0]
+            return candidates.find { it.version == '2' }
         }
         0 * conflictResolver._
 
@@ -372,7 +385,7 @@ class DependencyGraphBuilderTest extends Specification {
         doesNotResolve b, c
 
         when:
-        def result = builder.resolve(configuration, resolveData, listener)
+        def result = resolve()
         result.rethrowFailure()
 
         then:
@@ -387,12 +400,13 @@ class DependencyGraphBuilderTest extends Specification {
         doesNotResolve root, evicted
 
         when:
-        def result = builder.resolve(configuration, resolveData, listener)
+        def result = resolve()
         result.rethrowFailure()
 
         then:
-        1 * conflictResolver.select(!null, !null) >> { Collection<ModuleRevisionResolveState> candidates, ModuleRevisionResolveState root ->
-            return candidates.find { it.revision == '1.2' }
+        1 * conflictResolver.select(!null) >> {
+            Collection<ModuleRevisionResolveState> candidates = it[0]
+            return candidates.find { it.version == '1.2' }
         }
 
         and:
@@ -412,7 +426,7 @@ class DependencyGraphBuilderTest extends Specification {
         doesNotResolve d, c
 
         when:
-        def result = builder.resolve(configuration, resolveData, listener)
+        def result = resolve()
         result.rethrowFailure()
 
         then:
@@ -433,7 +447,7 @@ class DependencyGraphBuilderTest extends Specification {
         traverses d, c
 
         when:
-        def result = builder.resolve(configuration, resolveData, listener)
+        def result = resolve()
         result.rethrowFailure()
 
         then:
@@ -452,7 +466,7 @@ class DependencyGraphBuilderTest extends Specification {
         doesNotResolve c, a
 
         when:
-        def result = builder.resolve(configuration, resolveData, listener)
+        def result = resolve()
         result.rethrowFailure()
 
         then:
@@ -475,7 +489,7 @@ class DependencyGraphBuilderTest extends Specification {
         doesNotResolve d, e
 
         when:
-        def result = builder.resolve(configuration, resolveData, listener)
+        def result = resolve()
         result.rethrowFailure()
 
         then:
@@ -494,7 +508,7 @@ class DependencyGraphBuilderTest extends Specification {
         traverses a, b
 
         when:
-        def result = builder.resolve(configuration, resolveData, listener)
+        def result = resolve()
         result.rethrowFailure()
 
         then:
@@ -512,7 +526,7 @@ class DependencyGraphBuilderTest extends Specification {
         doesNotResolve b, c
 
         when:
-        def result = builder.resolve(configuration, resolveData, listener)
+        def result = resolve()
         result.rethrowFailure()
 
         then:
@@ -532,7 +546,7 @@ class DependencyGraphBuilderTest extends Specification {
         doesNotResolve b, c
 
         when:
-        def result = builder.resolve(configuration, resolveData, listener)
+        def result = resolve()
 
         then:
         result.unresolvedModuleDependencies.size() == 1
@@ -562,7 +576,7 @@ class DependencyGraphBuilderTest extends Specification {
         brokenSelector a, 'unknown'
 
         when:
-        def result = builder.resolve(configuration, resolveData, listener)
+        def result = resolve()
 
         then:
         result.unresolvedModuleDependencies.size() == 1
@@ -591,7 +605,7 @@ class DependencyGraphBuilderTest extends Specification {
         doesNotResolve b, c
 
         when:
-        def result = builder.resolve(configuration, resolveData, listener)
+        def result = resolve()
 
         then:
         result.unresolvedModuleDependencies.size() == 1
@@ -621,7 +635,7 @@ class DependencyGraphBuilderTest extends Specification {
         doesNotResolve b, c
 
         when:
-        def result = builder.resolve(configuration, resolveData, listener)
+        def result = resolve()
 
         then:
         result.unresolvedModuleDependencies.size() == 1
@@ -651,7 +665,7 @@ class DependencyGraphBuilderTest extends Specification {
         doesNotResolve b, c
 
         when:
-        def result = builder.resolve(configuration, resolveData, listener)
+        def result = resolve()
 
         then:
         result.unresolvedModuleDependencies.size() == 1
@@ -680,7 +694,7 @@ class DependencyGraphBuilderTest extends Specification {
         traversesMissing b, c
 
         when:
-        def result = builder.resolve(configuration, resolveData, listener)
+        def result = resolve()
 
         then:
         result.unresolvedModuleDependencies.size() == 1
@@ -714,11 +728,12 @@ class DependencyGraphBuilderTest extends Specification {
         doesNotResolve selected, c
 
         when:
-        def result = builder.resolve(configuration, resolveData, listener)
+        def result = resolve()
 
         then:
-        1 * conflictResolver.select(!null, !null) >> { Collection<ModuleRevisionResolveState> candidates, ModuleRevisionResolveState root ->
-            return candidates.find { it.revision == '1.2' }
+        1 * conflictResolver.select(!null) >> {
+            Collection<ModuleRevisionResolveState> candidates = it[0]
+            return candidates.find { it.version == '1.2' }
         }
 
         when:
@@ -744,18 +759,19 @@ class DependencyGraphBuilderTest extends Specification {
         traversesMissing b, selected
 
         when:
-        def result = builder.resolve(configuration, resolveData, listener)
+        def result = resolve()
         result.rethrowFailure()
 
         then:
-        1 * conflictResolver.select(!null, !null) >> { Collection<ModuleRevisionResolveState> candidates, ModuleRevisionResolveState root ->
-            return candidates.find { it.revision == '1.2' }
+        1 * conflictResolver.select(!null) >> {
+            Collection<ModuleRevisionResolveState> candidates = it[0]
+            return candidates.find { it.version == '1.2' }
         }
 
         and:
         ResolveException e = thrown()
         e.cause instanceof ModuleVersionNotFoundException
-        e.cause.message.contains("group:root:1.0 > group:b:1.0")
+        e.cause.message.contains("group:root:1.0")
     }
 
     def "does not fail when conflict resolution evicts a version that does not exist"() {
@@ -768,12 +784,13 @@ class DependencyGraphBuilderTest extends Specification {
         traverses b, selected
 
         when:
-        def result = builder.resolve(configuration, resolveData, listener)
+        def result = resolve()
         result.rethrowFailure()
 
         then:
-        1 * conflictResolver.select(!null, !null) >> { Collection<ModuleRevisionResolveState> candidates, ModuleRevisionResolveState root ->
-            return candidates.find { it.revision == '1.2' }
+        1 * conflictResolver.select(!null) >> {
+            Collection<ModuleRevisionResolveState> candidates = it[0]
+            return candidates.find { it.version == '1.2' }
         }
 
         and:
@@ -792,12 +809,13 @@ class DependencyGraphBuilderTest extends Specification {
         traverses c, selected
 
         when:
-        def result = builder.resolve(configuration, resolveData, listener)
+        def result = resolve()
         result.rethrowFailure()
 
         then:
-        1 * conflictResolver.select(!null, !null) >> { Collection<ModuleRevisionResolveState> candidates, ModuleRevisionResolveState root ->
-            return candidates.find { it.revision == '1.2' }
+        1 * conflictResolver.select(!null) >> {
+            Collection<ModuleRevisionResolveState> candidates = it[0]
+            return candidates.find { it.version == '1.2' }
         }
 
         and:
@@ -814,7 +832,7 @@ class DependencyGraphBuilderTest extends Specification {
         doesNotResolve b, evicted
 
         when:
-        def result = builder.resolve(configuration, resolveData, listener)
+        def result = resolve()
         result.rethrowFailure()
 
         then:
@@ -823,7 +841,7 @@ class DependencyGraphBuilderTest extends Specification {
 
     def revision(String name, String revision = '1.0') {
         DefaultModuleDescriptor descriptor = new DefaultModuleDescriptor(new ModuleRevisionId(new ModuleId("group", name), revision), "release", new Date())
-        DefaultBuildableModuleVersionMetaData metaData = new DefaultBuildableModuleVersionMetaData()
+        DefaultBuildableModuleVersionMetaDataResolveResult metaData = new DefaultBuildableModuleVersionMetaDataResolveResult()
         metaData.resolved(descriptor, false, null)
         config(metaData, 'default')
         descriptor.addArtifact('default', new DefaultArtifact(descriptor.moduleRevisionId, new Date(), "art1", "art", "zip"))
@@ -841,15 +859,15 @@ class DependencyGraphBuilderTest extends Specification {
         def idResolveResult = selectorResolvesTo(descriptor, to.id);
         ModuleVersionResolveResult resolveResult = Mock()
         1 * idResolveResult.resolve() >> resolveResult
-        1 * resolveResult.id >> to.id
-        1 * resolveResult.metaData >> to
+        _ * resolveResult.id >> to.id
+        _ * resolveResult.metaData >> to
     }
 
     def doesNotResolve(Map<String, ?> args = [:], ModuleVersionMetaData from, ModuleVersionMetaData to) {
         def descriptor = dependsOn(args, from.descriptor, to.descriptor.moduleRevisionId)
         ModuleVersionIdResolveResult result = Mock()
         (0..1) * dependencyResolver.resolve({it.descriptor == descriptor}) >> result
-        (0..1) * result.id >> to.id;
+        _ * result.id >> to.id;
         _ * result.failure >> null
         _ * result.selectionReason >> null
         0 * result._
@@ -860,7 +878,7 @@ class DependencyGraphBuilderTest extends Specification {
         def idResolveResult = selectorResolvesTo(descriptor, to.id)
         ModuleVersionResolveResult resolveResult = Mock()
         1 * idResolveResult.resolve() >> resolveResult
-        1 * resolveResult.id >> { throw new ModuleVersionNotFoundException(newId("org", "a", "1.2")) }
+        _ * resolveResult.failure >> { return new ModuleVersionNotFoundException(newId("org", "a", "1.2")) }
     }
 
     def traversesBroken(Map<String, ?> args = [:], ModuleVersionMetaData from, ModuleVersionMetaData to) {
@@ -868,7 +886,7 @@ class DependencyGraphBuilderTest extends Specification {
         def idResolveResult = selectorResolvesTo(descriptor, to.id)
         ModuleVersionResolveResult resolveResult = Mock()
         1 * idResolveResult.resolve() >> resolveResult
-        1 * resolveResult.id >> { throw new ModuleVersionResolveException(newSelector("a", "b", "c"), "broken") }
+        _ * resolveResult.failure >> { return new ModuleVersionResolveException(newSelector("a", "b", "c"), "broken") }
     }
 
     ModuleVersionIdentifier toModuleVersionIdentifier(ModuleRevisionId moduleRevisionId) {
diff --git a/subprojects/core-impl/src/test/groovy/org/gradle/api/internal/artifacts/ivyservice/resolveengine/VersionSelectionReasonResolverTest.groovy b/subprojects/core-impl/src/test/groovy/org/gradle/api/internal/artifacts/ivyservice/resolveengine/VersionSelectionReasonResolverTest.groovy
index 062c409..ff4ada1 100644
--- a/subprojects/core-impl/src/test/groovy/org/gradle/api/internal/artifacts/ivyservice/resolveengine/VersionSelectionReasonResolverTest.groovy
+++ b/subprojects/core-impl/src/test/groovy/org/gradle/api/internal/artifacts/ivyservice/resolveengine/VersionSelectionReasonResolverTest.groovy
@@ -16,30 +16,26 @@
 
 package org.gradle.api.internal.artifacts.ivyservice.resolveengine
 
-import spock.lang.Specification
 import org.gradle.api.internal.artifacts.ivyservice.resolveengine.result.VersionSelectionReasons
+import spock.lang.Specification
 
-/**
- * by Szczepan Faber, created at: 1/29/13
- */
 class VersionSelectionReasonResolverTest extends Specification {
 
     def "configures selection reason"() {
         def delegate = Mock(ModuleConflictResolver)
         VersionSelectionReasonResolver resolver = new VersionSelectionReasonResolver(delegate)
 
-        def root = Mock(ModuleRevisionResolveState)
         def candidate1 = Mock(ModuleRevisionResolveState)
         def candidate2 = Mock(ModuleRevisionResolveState)
 
         when:
-        def out = resolver.select([candidate1, candidate2], root)
+        def out = resolver.select([candidate1, candidate2])
 
         then:
         out == candidate2
 
         and:
-        1 * delegate.select([candidate1, candidate2], root) >> candidate2
+        1 * delegate.select([candidate1, candidate2]) >> candidate2
         1 * candidate2.getSelectionReason() >> VersionSelectionReasons.REQUESTED
         1 * candidate2.setSelectionReason(VersionSelectionReasons.CONFLICT_RESOLUTION)
         0 * _._
diff --git a/subprojects/core-impl/src/test/groovy/org/gradle/api/internal/artifacts/ivyservice/resolveengine/result/CachingDependencyResultFactoryTest.groovy b/subprojects/core-impl/src/test/groovy/org/gradle/api/internal/artifacts/ivyservice/resolveengine/result/CachingDependencyResultFactoryTest.groovy
index 1c1521c..428da60 100644
--- a/subprojects/core-impl/src/test/groovy/org/gradle/api/internal/artifacts/ivyservice/resolveengine/result/CachingDependencyResultFactoryTest.groovy
+++ b/subprojects/core-impl/src/test/groovy/org/gradle/api/internal/artifacts/ivyservice/resolveengine/result/CachingDependencyResultFactoryTest.groovy
@@ -16,16 +16,13 @@
 
 package org.gradle.api.internal.artifacts.ivyservice.resolveengine.result
 
+import org.gradle.api.artifacts.result.ModuleVersionSelectionReason
+import org.gradle.api.internal.artifacts.ivyservice.ModuleVersionResolveException
 import spock.lang.Specification
 
 import static org.gradle.api.internal.artifacts.DefaultModuleVersionSelector.newSelector
 import static org.gradle.api.internal.artifacts.result.ResolutionResultDataBuilder.newModule
-import org.gradle.api.internal.artifacts.ivyservice.ModuleVersionResolveException
-import org.gradle.api.artifacts.result.ModuleVersionSelectionReason
 
-/**
- * by Szczepan Faber, created at: 10/1/12
- */
 class CachingDependencyResultFactoryTest extends Specification {
 
     CachingDependencyResultFactory factory = new CachingDependencyResultFactory()
diff --git a/subprojects/core-impl/src/test/groovy/org/gradle/api/internal/artifacts/ivyservice/resolveengine/result/DummyBinaryStore.groovy b/subprojects/core-impl/src/test/groovy/org/gradle/api/internal/artifacts/ivyservice/resolveengine/result/DummyBinaryStore.groovy
new file mode 100644
index 0000000..b20b89a
--- /dev/null
+++ b/subprojects/core-impl/src/test/groovy/org/gradle/api/internal/artifacts/ivyservice/resolveengine/result/DummyBinaryStore.groovy
@@ -0,0 +1,48 @@
+/*
+ * Copyright 2013 the original author or authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY 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.resolveengine.result
+
+import org.gradle.api.internal.cache.BinaryStore
+
+public class DummyBinaryStore implements BinaryStore {
+
+    private final ByteArrayOutputStream bytes = new ByteArrayOutputStream()
+    private DataOutputStream output = new DataOutputStream(bytes)
+
+    void write(BinaryStore.WriteAction write) {
+        write.write(output)
+    }
+
+    String diagnose() {
+        "dummy in-memory binary store"
+    }
+
+    BinaryStore.BinaryData done() {
+        new BinaryStore.BinaryData() {
+            DataInputStream input
+            def <T> T read(BinaryStore.ReadAction<T> readAction) {
+                if (input == null) {
+                    input = new DataInputStream(new ByteArrayInputStream(bytes.toByteArray()))
+                }
+                readAction.read(input)
+            }
+
+            void done() {
+                input = null
+            }
+        }
+    }
+}
\ No newline at end of file
diff --git a/subprojects/core-impl/src/test/groovy/org/gradle/api/internal/artifacts/ivyservice/resolveengine/result/DummyStore.groovy b/subprojects/core-impl/src/test/groovy/org/gradle/api/internal/artifacts/ivyservice/resolveengine/result/DummyStore.groovy
new file mode 100644
index 0000000..03ee7aa
--- /dev/null
+++ b/subprojects/core-impl/src/test/groovy/org/gradle/api/internal/artifacts/ivyservice/resolveengine/result/DummyStore.groovy
@@ -0,0 +1,24 @@
+/*
+ * Copyright 2013 the original author or authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY 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.resolveengine.result
+
+import org.gradle.api.internal.cache.Store
+
+class DummyStore implements Store<Object> {
+    Object load(org.gradle.internal.Factory<Object> createIfNotPresent) {
+        return createIfNotPresent.create();
+    }
+}
diff --git a/subprojects/core-impl/src/test/groovy/org/gradle/api/internal/artifacts/ivyservice/resolveengine/result/InternalDependencyResultSerializerTest.groovy b/subprojects/core-impl/src/test/groovy/org/gradle/api/internal/artifacts/ivyservice/resolveengine/result/InternalDependencyResultSerializerTest.groovy
new file mode 100644
index 0000000..afa114d
--- /dev/null
+++ b/subprojects/core-impl/src/test/groovy/org/gradle/api/internal/artifacts/ivyservice/resolveengine/result/InternalDependencyResultSerializerTest.groovy
@@ -0,0 +1,80 @@
+/*
+ * Copyright 2013 the original author or authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY 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.resolveengine.result
+
+import org.gradle.api.artifacts.ModuleVersionSelector
+import org.gradle.api.internal.artifacts.ivyservice.ModuleVersionResolveException
+import org.gradle.messaging.serialize.InputStreamBackedDecoder
+import org.gradle.messaging.serialize.OutputStreamBackedEncoder
+import spock.lang.Specification
+
+import static org.gradle.api.internal.artifacts.DefaultModuleVersionIdentifier.newId
+import static org.gradle.api.internal.artifacts.DefaultModuleVersionSelector.newSelector
+
+class InternalDependencyResultSerializerTest extends Specification {
+
+    def serializer = new InternalDependencyResultSerializer()
+
+    def "serializes successful dependency result"() {
+        def successful = Mock(InternalDependencyResult) {
+            getRequested() >> newSelector("org", "foo", "1.0")
+            getFailure() >> null
+            getSelected() >> new DefaultModuleVersionSelection(newId("org", "foo", "1.0"), VersionSelectionReasons.REQUESTED)
+            getReason() >> VersionSelectionReasons.REQUESTED
+        }
+
+        when:
+        def bytes = new ByteArrayOutputStream()
+        def encoder = new OutputStreamBackedEncoder(bytes)
+        serializer.write(encoder, successful)
+        encoder.flush()
+        def out = serializer.read(new InputStreamBackedDecoder(new ByteArrayInputStream(bytes.toByteArray())), [:])
+
+        then:
+        out.requested == newSelector("org", "foo", "1.0")
+        out.failure == null
+        out.selected.selectedId == newId("org", "foo", "1.0")
+        out.selected.selectionReason == VersionSelectionReasons.REQUESTED
+    }
+
+    def "serializes failed dependency result"() {
+        ModuleVersionSelector requested = newSelector("x", "y", "1.0")
+        def failure = new ModuleVersionResolveException(newSelector("x", "y", "1.2"), new RuntimeException("Boo!"))
+
+        def failed = Mock(InternalDependencyResult) {
+            getRequested() >> requested
+            getFailure() >> failure
+            getSelected() >> null
+            getReason() >> VersionSelectionReasons.CONFLICT_RESOLUTION
+        }
+
+        when:
+        def bytes = new ByteArrayOutputStream()
+        def encoder = new OutputStreamBackedEncoder(bytes)
+        serializer.write(encoder, failed)
+        encoder.flush()
+        Map<ModuleVersionSelector, ModuleVersionResolveException> map = new HashMap<>()
+        map.put(requested, failure)
+        def out = serializer.read(new InputStreamBackedDecoder(new ByteArrayInputStream(bytes.toByteArray())), map)
+
+        then:
+        out.requested == newSelector("x", "y", "1.0")
+        out.failure.cause.message == "Boo!"
+        out.selected == null
+        out.reason == VersionSelectionReasons.CONFLICT_RESOLUTION
+    }
+}
diff --git a/subprojects/core-impl/src/test/groovy/org/gradle/api/internal/artifacts/ivyservice/resolveengine/result/ModuleVersionSelectionReasonSerializerTest.groovy b/subprojects/core-impl/src/test/groovy/org/gradle/api/internal/artifacts/ivyservice/resolveengine/result/ModuleVersionSelectionReasonSerializerTest.groovy
new file mode 100644
index 0000000..e05047b
--- /dev/null
+++ b/subprojects/core-impl/src/test/groovy/org/gradle/api/internal/artifacts/ivyservice/resolveengine/result/ModuleVersionSelectionReasonSerializerTest.groovy
@@ -0,0 +1,54 @@
+/*
+ * Copyright 2013 the original author or authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY 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.resolveengine.result
+
+import org.gradle.api.artifacts.result.ModuleVersionSelectionReason
+import org.gradle.messaging.serialize.InputStreamBackedDecoder
+import org.gradle.messaging.serialize.SerializerSpec
+
+class ModuleVersionSelectionReasonSerializerTest extends SerializerSpec {
+
+    private serializer = new ModuleVersionSelectionReasonSerializer()
+
+    def "serializes"() {
+        expect:
+        check(VersionSelectionReasons.CONFLICT_RESOLUTION)
+        check(VersionSelectionReasons.CONFLICT_RESOLUTION_BY_RULE)
+        check(VersionSelectionReasons.FORCED)
+        check(VersionSelectionReasons.REQUESTED)
+        check(VersionSelectionReasons.ROOT)
+        check(VersionSelectionReasons.SELECTED_BY_RULE)
+    }
+
+    def "yields informative message on incorrect input"() {
+        when:
+        check({} as ModuleVersionSelectionReason)
+        then:
+        thrown(IllegalArgumentException)
+
+        when:
+        serializer.read(new InputStreamBackedDecoder(new ByteArrayInputStream("foo".bytes)))
+
+        then:
+        thrown(IllegalArgumentException)
+    }
+
+    void check(ModuleVersionSelectionReason reason) {
+        def result = serialize(reason, serializer)
+        assert result == reason
+    }
+}
diff --git a/subprojects/core-impl/src/test/groovy/org/gradle/api/internal/artifacts/ivyservice/resolveengine/result/ModuleVersionSelectionSerializerTest.groovy b/subprojects/core-impl/src/test/groovy/org/gradle/api/internal/artifacts/ivyservice/resolveengine/result/ModuleVersionSelectionSerializerTest.groovy
new file mode 100644
index 0000000..fe56683
--- /dev/null
+++ b/subprojects/core-impl/src/test/groovy/org/gradle/api/internal/artifacts/ivyservice/resolveengine/result/ModuleVersionSelectionSerializerTest.groovy
@@ -0,0 +1,37 @@
+/*
+ * Copyright 2013 the original author or authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY 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.resolveengine.result
+
+import org.gradle.messaging.serialize.SerializerSpec
+
+import static org.gradle.api.internal.artifacts.DefaultModuleVersionIdentifier.newId
+
+class ModuleVersionSelectionSerializerTest extends SerializerSpec {
+
+    def serializer = new ModuleVersionSelectionSerializer()
+
+    def "serializes"() {
+        def selection = new DefaultModuleVersionSelection(newId("org", "foo", "2.0"), VersionSelectionReasons.REQUESTED)
+
+        when:
+        def result = serialize(selection, serializer)
+
+        then:
+        result.selectionReason == VersionSelectionReasons.REQUESTED
+        result.selectedId == newId("org", "foo", "2.0")
+    }
+}
diff --git a/subprojects/core-impl/src/test/groovy/org/gradle/api/internal/artifacts/ivyservice/resolveengine/result/ResolutionResultBuilderSpec.groovy b/subprojects/core-impl/src/test/groovy/org/gradle/api/internal/artifacts/ivyservice/resolveengine/result/ResolutionResultBuilderSpec.groovy
index 68749dd..2d7341c 100644
--- a/subprojects/core-impl/src/test/groovy/org/gradle/api/internal/artifacts/ivyservice/resolveengine/result/ResolutionResultBuilderSpec.groovy
+++ b/subprojects/core-impl/src/test/groovy/org/gradle/api/internal/artifacts/ivyservice/resolveengine/result/ResolutionResultBuilderSpec.groovy
@@ -18,16 +18,14 @@ package org.gradle.api.internal.artifacts.ivyservice.resolveengine.result
 
 import org.gradle.api.artifacts.ModuleVersionIdentifier
 import org.gradle.api.artifacts.ModuleVersionSelector
-import spock.lang.Specification
 import org.gradle.api.artifacts.result.*
+import org.gradle.api.internal.artifacts.ivyservice.ModuleVersionResolveException
+import spock.lang.Specification
 
 import static org.gradle.api.internal.artifacts.DefaultModuleVersionIdentifier.newId
 import static org.gradle.api.internal.artifacts.DefaultModuleVersionSelector.newSelector
-import org.gradle.api.internal.artifacts.ivyservice.ModuleVersionResolveException
+import static org.gradle.api.internal.artifacts.ivyservice.resolveengine.result.ResolutionResultPrinter.printGraph
 
-/**
- * by Szczepan Faber, created at: 8/27/12
- */
 class ResolutionResultBuilderSpec extends Specification {
 
     def builder = new ResolutionResultBuilder()
@@ -46,10 +44,10 @@ class ResolutionResultBuilderSpec extends Specification {
         resolvedConf("leaf4", [])
 
         when:
-        def result = builder.getResult()
+        def result = builder.complete()
 
         then:
-        print(result.root) == """x:root:1
+        printGraph(result.root) == """x:root:1
   x:mid1:1 [root]
     x:leaf1:1 [mid1]
     x:leaf2:1 [mid1]
@@ -69,10 +67,10 @@ class ResolutionResultBuilderSpec extends Specification {
         resolvedConf("b3", [])
 
         when:
-        def result = builder.getResult()
+        def result = builder.complete()
 
         then:
-        print(result.root) == """x:a:1
+        printGraph(result.root) == """x:a:1
   x:b1:1 [a]
     x:b2:1 [a,b1]
       x:b3:1 [a,b1,b2]
@@ -90,10 +88,10 @@ class ResolutionResultBuilderSpec extends Specification {
         resolvedConf("c", [dep("a")])
 
         when:
-        def result = builder.getResult()
+        def result = builder.complete()
 
         then:
-        print(result.root) == """x:a:1
+        printGraph(result.root) == """x:a:1
   x:b:1 [a]
     x:c:1 [b]
       x:a:1 [c]
@@ -109,7 +107,7 @@ class ResolutionResultBuilderSpec extends Specification {
         resolvedConf("d", [])
 
         when:
-        def deps = builder.result.root.dependencies
+        def deps = builder.complete().root.dependencies
 
         then:
         def b = deps.find { it.selected.id.name == 'b' }
@@ -127,7 +125,7 @@ class ResolutionResultBuilderSpec extends Specification {
         resolvedConf("c", [dep("a")])
 
         when:
-        def a = builder.getResult().root
+        def a = builder.complete().root
 
         then:
         def b  = first(a.dependencies).selected
@@ -162,10 +160,10 @@ class ResolutionResultBuilderSpec extends Specification {
         resolvedConf("leaf2", [])
 
         when:
-        def result = builder.getResult()
+        def result = builder.complete()
 
         then:
-        print(result.root) == """x:root:1
+        printGraph(result.root) == """x:root:1
   x:mid1:1 [root]
     x:leaf1:1 [mid1]
     x:leaf2:1 [mid1]
@@ -182,7 +180,7 @@ class ResolutionResultBuilderSpec extends Specification {
         resolvedConf("mid1", [dep("leaf2", new RuntimeException("baz!"))])
 
         when:
-        def result = builder.getResult()
+        def result = builder.complete()
 
         then:
         def mid1 = first(result.root.dependencies)
@@ -198,10 +196,10 @@ class ResolutionResultBuilderSpec extends Specification {
         resolvedConf("c", [])
 
         when:
-        def result = builder.getResult()
+        def result = builder.complete()
 
         then:
-        print(result.root) == """x:a:1
+        printGraph(result.root) == """x:a:1
   x:b:1 [a]
   x:c:1 [a]
   x:U:1 -> x:U:1 - Could not resolve x:U:1.
@@ -230,30 +228,6 @@ class ResolutionResultBuilderSpec extends Specification {
         newId("x", module, "1")
     }
 
-    String print(ResolvedModuleVersionResult root) {
-        StringBuilder sb = new StringBuilder();
-        sb.append(root).append("\n");
-        for (DependencyResult d : root.getDependencies()) {
-            print(d, sb, new HashSet(), "  ");
-        }
-
-        sb.toString();
-    }
-
-    void print(DependencyResult dep, StringBuilder sb, Set visited, String indent) {
-        if (dep instanceof UnresolvedDependencyResult) {
-            sb.append(indent + dep + "\n");
-            return
-        }
-        if (!visited.add(dep.getSelected())) {
-            return
-        }
-        sb.append(indent + dep + " [" + dep.selected.dependents*.from.id.name.join(",") + "]\n");
-        for (ResolvedDependencyResult d : dep.getSelected().getDependencies()) {
-            print(d, sb, visited, "  " + indent);
-        }
-    }
-
     class DummyModuleVersionSelection implements ModuleVersionSelection {
         ModuleVersionIdentifier selectedId
         ModuleVersionSelectionReason selectionReason
diff --git a/subprojects/core-impl/src/test/groovy/org/gradle/api/internal/artifacts/ivyservice/resolveengine/result/ResolutionResultPrinter.groovy b/subprojects/core-impl/src/test/groovy/org/gradle/api/internal/artifacts/ivyservice/resolveengine/result/ResolutionResultPrinter.groovy
new file mode 100644
index 0000000..f59c6d7
--- /dev/null
+++ b/subprojects/core-impl/src/test/groovy/org/gradle/api/internal/artifacts/ivyservice/resolveengine/result/ResolutionResultPrinter.groovy
@@ -0,0 +1,47 @@
+/*
+ * Copyright 2013 the original author or authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY 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.resolveengine.result
+
+import org.gradle.api.artifacts.result.DependencyResult
+import org.gradle.api.artifacts.result.ResolvedModuleVersionResult
+import org.gradle.api.artifacts.result.UnresolvedDependencyResult;
+
+public class ResolutionResultPrinter {
+    private static void printNode(DependencyResult dep, StringBuilder sb, Set visited, String indent) {
+        if (dep instanceof UnresolvedDependencyResult) {
+            sb.append(indent + dep + "\n");
+            return
+        }
+        if (!visited.add(dep.getSelected())) {
+            return
+        }
+        String reason = dep.selected.selectionReason.conflictResolution? "(C)" : "";
+        sb.append(indent + dep + reason + " [" + dep.selected.dependents*.from.id.name.join(",") + "]\n");
+        for (DependencyResult d : dep.getSelected().getDependencies()) {
+            printNode(d, sb, visited, "  " + indent);
+        }
+    }
+
+    static String printGraph(ResolvedModuleVersionResult root) {
+        StringBuilder sb = new StringBuilder();
+        sb.append(root).append("\n");
+        for (DependencyResult d : root.getDependencies()) {
+            printNode(d, sb, new HashSet(), "  ");
+        }
+
+        sb.toString();
+    }
+}
diff --git a/subprojects/core-impl/src/test/groovy/org/gradle/api/internal/artifacts/ivyservice/resolveengine/result/StreamingResolutionResultBuilderTest.groovy b/subprojects/core-impl/src/test/groovy/org/gradle/api/internal/artifacts/ivyservice/resolveengine/result/StreamingResolutionResultBuilderTest.groovy
new file mode 100644
index 0000000..c1eec1b
--- /dev/null
+++ b/subprojects/core-impl/src/test/groovy/org/gradle/api/internal/artifacts/ivyservice/resolveengine/result/StreamingResolutionResultBuilderTest.groovy
@@ -0,0 +1,137 @@
+/*
+ * Copyright 2013 the original author or authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY 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.resolveengine.result
+
+import org.gradle.api.artifacts.result.ModuleVersionSelectionReason
+import org.gradle.api.internal.artifacts.ivyservice.ModuleVersionResolveException
+import spock.lang.Specification
+
+import static org.gradle.api.internal.artifacts.DefaultModuleVersionIdentifier.newId
+import static org.gradle.api.internal.artifacts.DefaultModuleVersionSelector.newSelector
+import static org.gradle.api.internal.artifacts.ivyservice.resolveengine.result.ResolutionResultPrinter.printGraph
+import static org.gradle.api.internal.artifacts.ivyservice.resolveengine.result.VersionSelectionReasons.CONFLICT_RESOLUTION
+import static org.gradle.api.internal.artifacts.ivyservice.resolveengine.result.VersionSelectionReasons.REQUESTED
+
+class StreamingResolutionResultBuilderTest extends Specification {
+
+    StreamingResolutionResultBuilder builder = new StreamingResolutionResultBuilder(new DummyBinaryStore(), new DummyStore())
+
+    def "result can be read multiple times"() {
+        builder.start(newId("org", "root", "1.0"))
+
+        when:
+        def result = builder.complete()
+
+        then:
+        with(result) {
+            root.id == newId("org", "root", "1.0")
+            root.selectionReason == VersionSelectionReasons.ROOT
+        }
+        printGraph(result.root) == """org:root:1.0
+"""
+    }
+
+    def "maintains graph in byte stream"() {
+        builder.start(newId("org", "root", "1.0"))
+
+        builder.resolvedModuleVersion(sel("org", "dep1", "2.0", CONFLICT_RESOLUTION))
+        builder.resolvedConfiguration(newId("org", "root", "1.0"), [
+                new DefaultInternalDependencyResult(newSelector("org", "dep1", "2.0"), sel("org", "dep1", "2.0", CONFLICT_RESOLUTION), CONFLICT_RESOLUTION, null),
+                new DefaultInternalDependencyResult(newSelector("org", "dep2", "3.0"), null, CONFLICT_RESOLUTION, new ModuleVersionResolveException(newSelector("org", "dep2", "3.0"), new RuntimeException("Boo!")))
+        ])
+
+        when:
+        def result = builder.complete()
+
+        then:
+        printGraph(result.root) == """org:root:1.0
+  org:dep1:2.0(C) [root]
+  org:dep2:3.0 -> org:dep2:3.0 - Could not resolve org:dep2:3.0.
+"""
+    }
+
+    def "visiting resolved module version again has no effect"() {
+        builder.start(newId("org", "root", "1.0"))
+        builder.resolvedModuleVersion(sel("org", "root", "1.0", REQUESTED)) //it's fine
+
+        builder.resolvedModuleVersion(sel("org", "dep1", "2.0", CONFLICT_RESOLUTION))
+        builder.resolvedModuleVersion(sel("org", "dep1", "2.0", REQUESTED)) //will be ignored
+
+        builder.resolvedConfiguration(newId("org", "root", "1.0"),
+                [new DefaultInternalDependencyResult(newSelector("org", "dep1", "2.0"), sel("org", "dep1", "2.0", CONFLICT_RESOLUTION), CONFLICT_RESOLUTION, null)])
+
+        when:
+        def result = builder.complete()
+
+        then:
+        printGraph(result.root) == """org:root:1.0
+  org:dep1:2.0(C) [root]
+"""
+    }
+
+    def "visiting resolved configuration again accumulates dependencies"() {
+        builder.start(newId("org", "root", "1.0"))
+
+        builder.resolvedModuleVersion(sel("org", "dep1", "2.0", REQUESTED))
+        builder.resolvedModuleVersion(sel("org", "dep2", "2.0", REQUESTED))
+
+        builder.resolvedConfiguration(newId("org", "root", "1.0"), [
+                new DefaultInternalDependencyResult(newSelector("org", "dep1", "2.0"), sel("org", "dep1", "2.0", REQUESTED), REQUESTED, null),
+        ])
+        builder.resolvedConfiguration(newId("org", "root", "1.0"), [
+                new DefaultInternalDependencyResult(newSelector("org", "dep2", "2.0"), sel("org", "dep2", "2.0", REQUESTED), REQUESTED, null),
+        ])
+
+        when:
+        def result = builder.complete()
+
+        then:
+        printGraph(result.root) == """org:root:1.0
+  org:dep1:2.0 [root]
+  org:dep2:2.0 [root]
+"""
+    }
+
+    def "dependency failures are remembered"() {
+        builder.start(newId("org", "root", "1.0"))
+
+        builder.resolvedModuleVersion(sel("org", "dep1", "2.0", REQUESTED))
+        builder.resolvedModuleVersion(sel("org", "dep2", "2.0", REQUESTED))
+
+        builder.resolvedConfiguration(newId("org", "root", "1.0"), [
+            new DefaultInternalDependencyResult(newSelector("org", "dep1", "2.0"), null, REQUESTED, new ModuleVersionResolveException(newSelector("org", "dep1", "1.0"), new RuntimeException())),
+            new DefaultInternalDependencyResult(newSelector("org", "dep2", "2.0"), sel("org", "dep2", "2.0", REQUESTED), REQUESTED, null),
+        ])
+        builder.resolvedConfiguration(newId("org", "dep2", "2.0"), [
+            new DefaultInternalDependencyResult(newSelector("org", "dep1", "5.0"), null, REQUESTED, new ModuleVersionResolveException(newSelector("org", "dep1", "5.0"), new RuntimeException())),
+        ])
+
+        when:
+        def result = builder.complete()
+
+        then:
+        printGraph(result.root) == """org:root:1.0
+  org:dep1:2.0 -> org:dep1:1.0 - Could not resolve org:dep1:1.0.
+  org:dep2:2.0 [root]
+    org:dep1:5.0 -> org:dep1:5.0 - Could not resolve org:dep1:5.0.
+"""
+    }
+
+    private DefaultModuleVersionSelection sel(String org, String name, String ver, ModuleVersionSelectionReason reason) {
+        new DefaultModuleVersionSelection(newId(org, name, ver), reason)
+    }
+}
diff --git a/subprojects/core-impl/src/test/groovy/org/gradle/api/internal/artifacts/ivyservice/resolveengine/result/VersionSelectionReasonsTest.groovy b/subprojects/core-impl/src/test/groovy/org/gradle/api/internal/artifacts/ivyservice/resolveengine/result/VersionSelectionReasonsTest.groovy
index 1584706..164491e 100644
--- a/subprojects/core-impl/src/test/groovy/org/gradle/api/internal/artifacts/ivyservice/resolveengine/result/VersionSelectionReasonsTest.groovy
+++ b/subprojects/core-impl/src/test/groovy/org/gradle/api/internal/artifacts/ivyservice/resolveengine/result/VersionSelectionReasonsTest.groovy
@@ -20,9 +20,6 @@ import spock.lang.Specification
 
 import static org.gradle.api.internal.artifacts.ivyservice.resolveengine.result.VersionSelectionReasons.*
 
-/**
- * by Szczepan Faber, created at: 1/29/13
- */
 class VersionSelectionReasonsTest extends Specification {
 
     def "decorates with conflict resolution"() {
diff --git a/subprojects/core-impl/src/test/groovy/org/gradle/api/internal/artifacts/ivyservice/resolveengine/store/CachedStoreFactoryTest.groovy b/subprojects/core-impl/src/test/groovy/org/gradle/api/internal/artifacts/ivyservice/resolveengine/store/CachedStoreFactoryTest.groovy
new file mode 100644
index 0000000..6bf7e78
--- /dev/null
+++ b/subprojects/core-impl/src/test/groovy/org/gradle/api/internal/artifacts/ivyservice/resolveengine/store/CachedStoreFactoryTest.groovy
@@ -0,0 +1,40 @@
+/*
+ * Copyright 2013 the original author or authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY 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.resolveengine.store
+
+import org.gradle.internal.Factory
+import spock.lang.Specification
+
+class CachedStoreFactoryTest extends Specification {
+
+    def "stores results"() {
+        def factory = new CachedStoreFactory("some cache")
+
+        def results1 = new Object()
+        def results2 = new Object()
+
+        def store1 = factory.createCachedStore("conf1")
+        def store1b = factory.createCachedStore("conf1")
+        def store2 = factory.createCachedStore("conf2")
+
+        expect:
+        store1.load({results1} as Factory) == results1
+        store1.load({assert false} as Factory) == results1
+        store1b.load({assert false} as Factory) == results1
+        store2.load({results2} as Factory) == results2
+    }
+}
diff --git a/subprojects/core-impl/src/test/groovy/org/gradle/api/internal/artifacts/ivyservice/resolveengine/store/DefaultBinaryStoreTest.groovy b/subprojects/core-impl/src/test/groovy/org/gradle/api/internal/artifacts/ivyservice/resolveengine/store/DefaultBinaryStoreTest.groovy
new file mode 100644
index 0000000..a00bbd4
--- /dev/null
+++ b/subprojects/core-impl/src/test/groovy/org/gradle/api/internal/artifacts/ivyservice/resolveengine/store/DefaultBinaryStoreTest.groovy
@@ -0,0 +1,82 @@
+/*
+ * Copyright 2011 the original author or authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY 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.resolveengine.store
+
+import org.gradle.api.internal.cache.BinaryStore
+import org.gradle.test.fixtures.file.TestNameTestDirectoryProvider
+import org.junit.Rule
+import spock.lang.Specification
+
+class DefaultBinaryStoreTest extends Specification {
+
+    @Rule TestNameTestDirectoryProvider temp = new TestNameTestDirectoryProvider()
+
+    def "stores binary data"() {
+        def store = new DefaultBinaryStore(temp.file("foo.bin"))
+
+        when:
+        store.write({ it.writeInt(10) } as BinaryStore.WriteAction)
+        store.write({ it.writeUTF("x") } as BinaryStore.WriteAction)
+        def data1 = store.done()
+        store.write({ it.writeUTF("y") } as BinaryStore.WriteAction)
+        def data2 = store.done()
+
+        then:
+        data1.read({ it.readInt() } as BinaryStore.ReadAction) == 10
+        data1.read({ it.readUTF() } as BinaryStore.ReadAction) == "x"
+        data1.done()
+
+        data2.read({ it.readUTF() } as BinaryStore.ReadAction) == "y"
+        data2.done()
+
+        cleanup:
+        store.close()
+    }
+
+    def "data can be re-read"() {
+        def store = new DefaultBinaryStore(temp.file("foo.bin"))
+
+        when:
+        store.write({ it.writeInt(10) } as BinaryStore.WriteAction)
+        store.write({ it.writeUTF("x") } as BinaryStore.WriteAction)
+        def data = store.done()
+
+        then:
+        data.read({ it.readInt() } as BinaryStore.ReadAction) == 10
+        data.read({ it.readUTF() } as BinaryStore.ReadAction) == "x"
+        data.done()
+
+        then:
+        data.read({ it.readInt() } as BinaryStore.ReadAction) == 10
+        data.read({ it.readUTF() } as BinaryStore.ReadAction) == "x"
+        data.done()
+
+        cleanup:
+        store.close()
+    }
+
+    def "may be empty"() {
+        def store = new DefaultBinaryStore(temp.file("foo.bin"))
+
+        when:
+        def data = store.done()
+        store.close()
+
+        then:
+        data.done()
+    }
+}
diff --git a/subprojects/core-impl/src/test/groovy/org/gradle/api/internal/artifacts/ivyservice/resolveengine/store/ResolutionResultsStoreFactoryTest.groovy b/subprojects/core-impl/src/test/groovy/org/gradle/api/internal/artifacts/ivyservice/resolveengine/store/ResolutionResultsStoreFactoryTest.groovy
new file mode 100644
index 0000000..0f2a270
--- /dev/null
+++ b/subprojects/core-impl/src/test/groovy/org/gradle/api/internal/artifacts/ivyservice/resolveengine/store/ResolutionResultsStoreFactoryTest.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.api.internal.artifacts.ivyservice.resolveengine.store
+
+import org.gradle.api.internal.cache.BinaryStore
+import org.gradle.api.internal.file.TmpDirTemporaryFileProvider
+import org.gradle.internal.CompositeStoppable
+import spock.lang.Specification
+
+class ResolutionResultsStoreFactoryTest extends Specification {
+
+    def f = new ResolutionResultsStoreFactory(new TmpDirTemporaryFileProvider())
+
+    def "provides binary stores"() {
+        def store1 = f.createBinaryStore("1")
+        def store2 = f.createBinaryStore("2")
+
+        expect:
+        store1 != store2
+        store1 == f.createBinaryStore("1")
+    }
+
+    def "rolls the file"() {
+        f = new ResolutionResultsStoreFactory(new TmpDirTemporaryFileProvider(), 2);
+
+        when:
+        def store = f.createBinaryStore("1")
+        store.write({it.writeByte(1); it.writeByte(2) } as BinaryStore.WriteAction)
+        store.done()
+        def store2 = f.createBinaryStore("1")
+
+        then:
+        store.file == store2.file
+
+        when:
+        store2.write({it.writeByte(4)} as BinaryStore.WriteAction)
+        store2.done()
+
+        then:
+        f.createBinaryStore("1").file != store2.file
+    }
+
+    def "cleans up binary files"() {
+        f = new ResolutionResultsStoreFactory(new TmpDirTemporaryFileProvider(), 1);
+
+        when:
+        def store = f.createBinaryStore("1")
+        store.write({it.writeByte(1); it.writeByte(2) } as BinaryStore.WriteAction)
+        store.done()
+        def store2 = f.createBinaryStore("1") //rolled
+        def store3 = f.createBinaryStore("2")
+
+        then:
+        store.file != store2.file //rolled
+        [store.file, store2.file, store3.file].each { it.exists() }
+
+        when:
+        new CompositeStoppable().add(store, store2, store3)
+
+        then:
+        [store.file, store2.file, store3.file].each { !it.exists() }
+    }
+
+    def "provides caches"() {
+        expect:
+        f.createNewModelCache("x").load({"x"} as org.gradle.internal.Factory) == "x"
+        f.createNewModelCache("y").load({"y"} as org.gradle.internal.Factory) == "y"
+        f.createNewModelCache("y").load({"yyyy"} as org.gradle.internal.Factory) == "y"
+
+        f.createOldModelCache("x").load({"x"} as org.gradle.internal.Factory) == "x"
+        f.createOldModelCache("y").load({"y"} as org.gradle.internal.Factory) == "y"
+        f.createOldModelCache("y").load({"yyyy"} as org.gradle.internal.Factory) == "y"
+    }
+}
diff --git a/subprojects/core-impl/src/test/groovy/org/gradle/api/internal/artifacts/mvnsettings/DefaultLocalMavenRepositoryLocatorTest.groovy b/subprojects/core-impl/src/test/groovy/org/gradle/api/internal/artifacts/mvnsettings/DefaultLocalMavenRepositoryLocatorTest.groovy
index 18896b8..32615ae 100644
--- a/subprojects/core-impl/src/test/groovy/org/gradle/api/internal/artifacts/mvnsettings/DefaultLocalMavenRepositoryLocatorTest.groovy
+++ b/subprojects/core-impl/src/test/groovy/org/gradle/api/internal/artifacts/mvnsettings/DefaultLocalMavenRepositoryLocatorTest.groovy
@@ -52,7 +52,7 @@ class DefaultLocalMavenRepositoryLocatorTest extends Specification {
         locator.localMavenRepository
         then:
         def ex = thrown(CannotLocateLocalMavenRepositoryException);
-        ex.message == "Unable to parse local maven settings."
+        ex.message == "Unable to parse local Maven settings."
         ex.cause.message.contains(settingsFile.absolutePath)
     }
 
@@ -64,7 +64,7 @@ class DefaultLocalMavenRepositoryLocatorTest extends Specification {
         locator.localMavenRepository
         then:
         def ex = thrown(CannotLocateLocalMavenRepositoryException)
-        ex.message == "Unable to parse local maven settings."
+        ex.message == "Unable to parse local Maven settings."
         ex.cause.message.contains(settingsFile.absolutePath)
     }
 
diff --git a/subprojects/core-impl/src/test/groovy/org/gradle/api/internal/artifacts/repositories/DefaultBaseRepositoryFactoryTest.groovy b/subprojects/core-impl/src/test/groovy/org/gradle/api/internal/artifacts/repositories/DefaultBaseRepositoryFactoryTest.groovy
index d3f1c8e..31ba2f6 100644
--- a/subprojects/core-impl/src/test/groovy/org/gradle/api/internal/artifacts/repositories/DefaultBaseRepositoryFactoryTest.groovy
+++ b/subprojects/core-impl/src/test/groovy/org/gradle/api/internal/artifacts/repositories/DefaultBaseRepositoryFactoryTest.groovy
@@ -16,138 +16,153 @@
 
 package org.gradle.api.internal.artifacts.repositories
 
-import org.apache.ivy.core.cache.RepositoryCacheManager
 import org.apache.ivy.plugins.resolver.DependencyResolver
 import org.gradle.api.InvalidUserDataException
 import org.gradle.api.artifacts.dsl.RepositoryHandler
 import org.gradle.api.artifacts.repositories.ArtifactRepository
+import org.gradle.api.internal.artifacts.ModuleMetadataProcessor
+import org.gradle.api.internal.artifacts.dsl.DefaultRepositoryHandler
+import org.gradle.api.internal.artifacts.ivyservice.ivyresolve.parser.MetaDataParser
 import org.gradle.api.internal.artifacts.mvnsettings.LocalMavenRepositoryLocator
+import org.gradle.api.internal.artifacts.repositories.legacy.LegacyDependencyResolverRepositoryFactory
 import org.gradle.api.internal.artifacts.repositories.transport.RepositoryTransportFactory
 import org.gradle.api.internal.externalresource.local.LocallyAvailableResourceFinder
 import org.gradle.api.internal.file.FileResolver
 import org.gradle.internal.reflect.DirectInstantiator
 import org.gradle.logging.ProgressLoggerFactory
-import org.gradle.util.JUnit4GroovyMockery
-import org.hamcrest.Matchers
-import org.jmock.integration.junit4.JMock
-import org.junit.Before
-import org.junit.Test
-import org.junit.runner.RunWith
-/**
- * @author Hans Dockter
- */
- at RunWith(JMock.class)
-class DefaultBaseRepositoryFactoryTest {
+import spock.lang.Specification
+
+class DefaultBaseRepositoryFactoryTest extends Specification {
     static final URI RESOLVER_URL = new URI('http://a.b.c/')
     static final String TEST_REPO = 'http://www.gradle.org'
     static final URI TEST_REPO_URL = new URI('http://www.gradle.org/')
     static final URI TEST_REPO2_URL = new URI('http://www.gradleware.com/')
 
-    final JUnit4GroovyMockery context = new JUnit4GroovyMockery()
-    final LocalMavenRepositoryLocator localMavenRepoLocator = context.mock(LocalMavenRepositoryLocator.class)
-    final FileResolver fileResolver = context.mock(FileResolver.class)
-    final RepositoryTransportFactory transportFactory = context.mock(RepositoryTransportFactory.class)
-    final LocallyAvailableResourceFinder locallyAvailableResourceFinder = context.mock(LocallyAvailableResourceFinder.class)
-    final RepositoryCacheManager localCacheManager = context.mock(RepositoryCacheManager)
-    final RepositoryCacheManager downloadingCacheManager = context.mock(RepositoryCacheManager)
-    final ProgressLoggerFactory progressLoggerFactory = context.mock(ProgressLoggerFactory)
+    final LocalMavenRepositoryLocator localMavenRepoLocator = Mock()
+    final FileResolver fileResolver = Mock()
+    final RepositoryTransportFactory transportFactory = Mock()
+    final LocallyAvailableResourceFinder locallyAvailableResourceFinder = Mock()
+    final ProgressLoggerFactory progressLoggerFactory = Mock()
+    final MetaDataParser metaDataParser = Mock()
+    final ModuleMetadataProcessor metadataProcessor = Mock()
+    final LegacyDependencyResolverRepositoryFactory legacyDependencyResolverRepositoryFactory = Mock()
+
 
     final DefaultBaseRepositoryFactory factory = new DefaultBaseRepositoryFactory(
             localMavenRepoLocator, fileResolver, new DirectInstantiator(), transportFactory, locallyAvailableResourceFinder,
-            progressLoggerFactory, localCacheManager, downloadingCacheManager
+            metaDataParser, metadataProcessor, legacyDependencyResolverRepositoryFactory
     )
 
-    @Before public void setup() {
-        context.checking {
-            allowing(fileResolver).resolveUri('uri');
-            will(returnValue(RESOLVER_URL))
-            allowing(fileResolver).resolveUri(TEST_REPO);
-            will(returnValue(TEST_REPO_URL))
-            allowing(fileResolver).resolveUri('uri2');
-            will(returnValue(TEST_REPO2_URL))
-            allowing(fileResolver).resolveUri(withParam(Matchers.instanceOf(URI)));
-            will { uri -> return uri }
-        }
-    }
+    def testCreateResolverWithStringDescription() {
+        when:
+        fileResolver.resolveUri('uri') >> RESOLVER_URL
 
-    @Test public void testCreateResolverWithStringDescription() {
+        then:
         def repository = factory.createRepository('uri')
 
-        assert repository instanceof DefaultMavenArtifactRepository
-        assert repository.url == RESOLVER_URL
-        assert repository.name == null
-        assert repository.artifactUrls.isEmpty()
+        repository instanceof DefaultMavenArtifactRepository
+        repository.name == null
+        repository.url == RESOLVER_URL
+        repository.artifactUrls.isEmpty()
     }
 
-    @Test public void testCreateResolverWithMapDescription() {
+    def testCreateResolverWithMapDescription() {
+        when:
+        fileResolver.resolveUri('uri') >> RESOLVER_URL
+
+        then:
         def repository = factory.createRepository([name: 'name', url: 'uri'])
 
-        assert repository instanceof DefaultMavenArtifactRepository
-        assert repository.url == RESOLVER_URL
-        assert repository.name == 'name'
-        assert repository.artifactUrls.isEmpty()
+        repository instanceof DefaultMavenArtifactRepository
+        repository.name == 'name'
+        repository.url == RESOLVER_URL
+        repository.artifactUrls.isEmpty()
     }
 
-    @Test public void testCreateResolverWithResolverDescription() {
-        DependencyResolver resolver = context.mock(DependencyResolver)
-        
-        ArtifactRepository repository = factory.createRepository(resolver)
+    def testCreateResolverWithResolverDescription() {
+        def repository = Mock(ArtifactRepository)
+        def resolver = Mock(DependencyResolver)
+
+        when:
+        legacyDependencyResolverRepositoryFactory.createRepository(resolver) >> repository
 
-        assert repository instanceof FixedResolverArtifactRepository
-        assert repository.resolver == resolver
+        then:
+        factory.createRepository(resolver) == repository
     }
 
-    @Test public void testCreateResolverWithArtifactRepositoryDescription() {
-        ArtifactRepository repo = context.mock(ArtifactRepository)
+    def testCreateResolverWithArtifactRepositoryDescription() {
+        when:
+        ArtifactRepository repo = Mock(ArtifactRepository)
 
-        assert factory.createRepository(repo) == repo
+        then:
+        factory.createRepository(repo) == repo
     }
 
-    @Test(expected = InvalidUserDataException) public void testCreateResolverForUnknownDescription() {
+    def testCreateResolverForUnknownDescription() {
+        when:
         def someIllegalDescription = new NullPointerException()
+
         factory.createRepository(someIllegalDescription)
+
+        then:
+        thrown InvalidUserDataException
     }
 
-    @Test public void testCreateFlatDirResolver() {
+    def testCreateFlatDirResolver() {
+        expect:
         def repo =factory.createFlatDirRepository()
-        assert repo instanceof DefaultFlatDirArtifactRepository
+        repo instanceof DefaultFlatDirArtifactRepository
     }
 
-    @Test public void testCreateLocalMavenRepo() {
+    def testCreateLocalMavenRepo() {
+        given:
         File repoDir = new File(".m2/repository")
 
-        context.checking {
-            one(localMavenRepoLocator).getLocalMavenRepository()
-            will(returnValue(repoDir))
-            allowing(fileResolver).resolveUri(repoDir)
-            will(returnValue(repoDir.toURI()))
-        }
+        when:
+        localMavenRepoLocator.getLocalMavenRepository() >> repoDir
+        fileResolver.resolveUri(repoDir) >> repoDir.toURI()
 
+        then:
         def repo = factory.createMavenLocalRepository()
-        assert repo instanceof DefaultMavenArtifactRepository
-        assert repo.url == repoDir.toURI()
+        repo instanceof DefaultMavenArtifactRepository
+        repo.url == repoDir.toURI()
+    }
+
+    def testCreateJCenterRepo() {
+        given:
+        def jcenterUrl = new URI(DefaultRepositoryHandler.BINTRAY_JCENTER_URL)
+
+        when:
+        fileResolver.resolveUri(DefaultRepositoryHandler.BINTRAY_JCENTER_URL) >> jcenterUrl
+
+        then:
+        def repo = factory.createJCenterRepository()
+        repo instanceof DefaultMavenArtifactRepository
+        repo.url == jcenterUrl
     }
 
-    @Test public void testCreateMavenCentralRepo() {
+    def testCreateMavenCentralRepo() {
+        given:
         def centralUrl = new URI(RepositoryHandler.MAVEN_CENTRAL_URL)
 
-        context.checking {
-            allowing(fileResolver).resolveUri(RepositoryHandler.MAVEN_CENTRAL_URL)
-            will(returnValue(centralUrl))
-        }
+        when:
+        fileResolver.resolveUri(RepositoryHandler.MAVEN_CENTRAL_URL) >> centralUrl
 
+        then:
         def repo = factory.createMavenCentralRepository()
-        assert repo instanceof DefaultMavenArtifactRepository
-        assert repo.url == centralUrl
+        repo instanceof DefaultMavenArtifactRepository
+        repo.url == centralUrl
     }
 
-    @Test public void createIvyRepository() {
+    def createIvyRepository() {
+        expect:
         def repo = factory.createIvyRepository()
-        assert repo instanceof DefaultIvyArtifactRepository
+        repo instanceof DefaultIvyArtifactRepository
     }
 
-    @Test public void createMavenRepository() {
+    def createMavenRepository() {
+        expect:
         def repo = factory.createMavenRepository()
-        assert repo instanceof DefaultMavenArtifactRepository
+        repo instanceof DefaultMavenArtifactRepository
     }
 }
diff --git a/subprojects/core-impl/src/test/groovy/org/gradle/api/internal/artifacts/repositories/DefaultFlatDirArtifactRepositoryTest.groovy b/subprojects/core-impl/src/test/groovy/org/gradle/api/internal/artifacts/repositories/DefaultFlatDirArtifactRepositoryTest.groovy
index 9fcf452..1ca8535 100644
--- a/subprojects/core-impl/src/test/groovy/org/gradle/api/internal/artifacts/repositories/DefaultFlatDirArtifactRepositoryTest.groovy
+++ b/subprojects/core-impl/src/test/groovy/org/gradle/api/internal/artifacts/repositories/DefaultFlatDirArtifactRepositoryTest.groovy
@@ -14,33 +14,49 @@
  * limitations under the License.
  */
 package org.gradle.api.internal.artifacts.repositories
-
-import org.apache.ivy.core.cache.RepositoryCacheManager
-import org.apache.ivy.plugins.resolver.FileSystemResolver
+import org.apache.ivy.core.module.id.ArtifactRevisionId
 import org.gradle.api.InvalidUserDataException
+import org.gradle.api.internal.artifacts.ModuleMetadataProcessor
+import org.gradle.api.internal.artifacts.ivyservice.ivyresolve.parser.MetaDataParser
+import org.gradle.api.internal.artifacts.repositories.resolver.IvyResolver
+import org.gradle.api.internal.artifacts.repositories.transport.RepositoryTransport
+import org.gradle.api.internal.artifacts.repositories.transport.RepositoryTransportFactory
+import org.gradle.api.internal.externalresource.local.LocallyAvailableResourceFinder
+import org.gradle.api.internal.externalresource.transport.ExternalResourceRepository
 import org.gradle.api.internal.file.FileResolver
 import org.gradle.api.internal.file.collections.SimpleFileCollection
 import spock.lang.Specification
 
 class DefaultFlatDirArtifactRepositoryTest extends Specification {
     final FileResolver fileResolver = Mock()
-    final RepositoryCacheManager localCacheManager = Mock()
-    final DefaultFlatDirArtifactRepository repository = new DefaultFlatDirArtifactRepository(fileResolver, localCacheManager)
+    final ExternalResourceRepository resourceRepository = Mock()
+    final RepositoryTransport repositoryTransport = Mock()
+    final RepositoryTransportFactory transportFactory = Mock()
+    final LocallyAvailableResourceFinder<ArtifactRevisionId> locallyAvailableResourceFinder = Mock()
+    final MetaDataParser metaDataParser = Mock()
+    final ModuleMetadataProcessor metadataProcessor = Mock()
+    final DefaultFlatDirArtifactRepository repository = new DefaultFlatDirArtifactRepository(
+            fileResolver, transportFactory, locallyAvailableResourceFinder, metaDataParser, metadataProcessor)
 
     def "creates a repository with multiple root directories"() {
         given:
         def dir1 = new File('a')
         def dir2 = new File('b')
         _ * fileResolver.resolveFiles(['a', 'b']) >> new SimpleFileCollection(dir1, dir2)
+        _ * repositoryTransport.repository >> resourceRepository
 
         and:
+        repository.name = 'repo-name'
         repository.dirs('a', 'b')
 
         when:
-        def repo = repository.createLegacyDslObject()
+        def repo = repository.createResolver()
 
         then:
-        repo instanceof FileSystemResolver
+        1 * transportFactory.createFileTransport("repo-name") >> repositoryTransport
+
+        and:
+        repo instanceof IvyResolver
         def expectedPatterns = [
                 "$dir1.absolutePath/[artifact]-[revision](-[classifier]).[ext]",
                 "$dir1.absolutePath/[artifact](-[classifier]).[ext]",
@@ -57,7 +73,7 @@ class DefaultFlatDirArtifactRepositoryTest extends Specification {
         _ * fileResolver.resolveFiles(_) >> new SimpleFileCollection()
 
         when:
-        repository.createLegacyDslObject()
+        repository.createResolver()
 
         then:
         InvalidUserDataException e = thrown()
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
index 44adf6b..e0f6a16 100644
--- 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
@@ -14,20 +14,16 @@
  * limitations under the License.
  */
 package org.gradle.api.internal.artifacts.repositories
-
-import org.apache.ivy.core.cache.RepositoryCacheManager
 import org.gradle.api.InvalidUserDataException
 import org.gradle.api.artifacts.repositories.PasswordCredentials
-import org.gradle.api.internal.artifacts.ivyservice.ivyresolve.ExternalResourceResolverAdapter
+import org.gradle.api.internal.artifacts.ModuleMetadataProcessor
+import org.gradle.api.internal.artifacts.ivyservice.ivyresolve.parser.MetaDataParser
 import org.gradle.api.internal.artifacts.repositories.resolver.IvyResolver
+import org.gradle.api.internal.artifacts.repositories.transport.RepositoryTransport
 import org.gradle.api.internal.artifacts.repositories.transport.RepositoryTransportFactory
-import org.gradle.api.internal.externalresource.cached.CachedExternalResourceIndex
 import org.gradle.api.internal.externalresource.local.LocallyAvailableResourceFinder
 import org.gradle.api.internal.externalresource.transport.ExternalResourceRepository
-import org.gradle.api.internal.externalresource.transport.file.FileTransport
-import org.gradle.api.internal.externalresource.transport.http.HttpTransport
 import org.gradle.api.internal.file.FileResolver
-import org.gradle.api.internal.file.TemporaryFileProvider
 import org.gradle.internal.reflect.DirectInstantiator
 import org.gradle.logging.ProgressLoggerFactory
 import spock.lang.Specification
@@ -36,13 +32,14 @@ class DefaultIvyArtifactRepositoryTest extends Specification {
     final FileResolver fileResolver = Mock()
     final PasswordCredentials credentials = Mock()
     final RepositoryTransportFactory transportFactory = Mock()
-    final RepositoryCacheManager cacheManager = Mock()
     final LocallyAvailableResourceFinder locallyAvailableResourceFinder = Mock()
-    final CachedExternalResourceIndex cachedExternalResourceIndex = Mock()
+    final ExternalResourceRepository resourceRepository = Mock()
     final ProgressLoggerFactory progressLoggerFactory = Mock()
+    final MetaDataParser metaDataParser = Mock()
+    final ModuleMetadataProcessor metadataProcessor = Mock()
 
     final DefaultIvyArtifactRepository repository = new DefaultIvyArtifactRepository(
-            fileResolver, credentials, transportFactory, locallyAvailableResourceFinder, new DirectInstantiator()
+            fileResolver, credentials, transportFactory, locallyAvailableResourceFinder, new DirectInstantiator(), metaDataParser, metadataProcessor
     )
 
     def "default values"() {
@@ -59,7 +56,7 @@ class DefaultIvyArtifactRepositoryTest extends Specification {
         fileResolver.resolveUri('pattern1') >> new URI('scheme:resource1')
 
         when:
-        repository.createRealResolver()
+        repository.createResolver()
 
         then:
         InvalidUserDataException e = thrown()
@@ -76,7 +73,7 @@ class DefaultIvyArtifactRepositoryTest extends Specification {
         fileResolver.resolveUri('pattern2') >> new URI('file:resource2')
 
         when:
-        repository.createRealResolver()
+        repository.createResolver()
 
         then:
         InvalidUserDataException e = thrown()
@@ -92,15 +89,15 @@ class DefaultIvyArtifactRepositoryTest extends Specification {
         given:
         fileResolver.resolveUri('http://host/') >> new URI('http://host/')
         fileResolver.resolveUri('http://other/') >> new URI('http://other/')
-        transportFactory.createHttpTransport('name', credentials) >> createHttpTransport("name", credentials)
+        transportFactory.createHttpTransport('name', credentials) >> transport()
 
         when:
-        def resolver = repository.createRealResolver()
+        def resolver = repository.createResolver()
 
         then:
         with(resolver) {
             it instanceof IvyResolver
-            repository instanceof ExternalResourceRepository
+            repository == resourceRepository
             name == 'name'
             artifactPatterns == ['http://host/[organisation]/[artifact]-[revision].[ext]', 'http://other/[module]/[artifact]-[revision].[ext]']
             ivyPatterns == ['http://host/[module]/ivy-[revision].xml']
@@ -117,18 +114,18 @@ class DefaultIvyArtifactRepositoryTest extends Specification {
 
         given:
         fileResolver.resolveUri('repo/') >> fileUri
-        transportFactory.createFileTransport('name') >> new FileTransport('name', cacheManager, Mock(TemporaryFileProvider))
+        transportFactory.createFileTransport('name') >> transport()
 
         when:
-        def resolver = repository.createRealResolver()
+        def resolver = repository.createResolver()
 
         then:
         with(resolver) {
             it instanceof IvyResolver
             repository instanceof ExternalResourceRepository
             name == 'name'
-            artifactPatterns == ["${file.absolutePath}/[organisation]/[artifact]-[revision].[ext]", "${file.absolutePath}/[organisation]/[module]/[artifact]-[revision].[ext]"]
-            ivyPatterns == ["${file.absolutePath}/[organisation]/[module]/ivy-[revision].xml"]
+            artifactPatterns == ["${fileUri}/[organisation]/[artifact]-[revision].[ext]", "${fileUri}/[organisation]/[module]/[artifact]-[revision].[ext]"]
+            ivyPatterns == ["${fileUri}/[organisation]/[module]/ivy-[revision].xml"]
         }
     }
 
@@ -140,7 +137,7 @@ class DefaultIvyArtifactRepositoryTest extends Specification {
 
         given:
         fileResolver.resolveUri('repo/') >> fileUri
-        transportFactory.createFileTransport('name') >> new FileTransport('name', cacheManager, Mock(TemporaryFileProvider))
+        transportFactory.createFileTransport('name') >> transport()
 
         when:
         def wrapper = repository.createLegacyDslObject()
@@ -155,8 +152,7 @@ class DefaultIvyArtifactRepositoryTest extends Specification {
         def repo = wrapper.createResolver()
 
         then:
-        repo instanceof ExternalResourceResolverAdapter
-        repo.resolver.is(wrapper.resolver)
+        repo.is(wrapper.resolver)
     }
 
     def "uses gradle patterns with specified url and default layout"() {
@@ -165,10 +161,10 @@ class DefaultIvyArtifactRepositoryTest extends Specification {
 
         given:
         fileResolver.resolveUri('http://host') >> new URI('http://host/')
-        transportFactory.createHttpTransport('name', credentials) >> createHttpTransport("name", credentials)
+        transportFactory.createHttpTransport('name', credentials) >> transport()
 
         when:
-        def resolver = repository.createRealResolver()
+        def resolver = repository.createResolver()
 
         then:
         with(resolver) {
@@ -187,10 +183,10 @@ class DefaultIvyArtifactRepositoryTest extends Specification {
 
         given:
         fileResolver.resolveUri('http://host') >> new URI('http://host/')
-        transportFactory.createHttpTransport('name', credentials) >> createHttpTransport("name", credentials)
+        transportFactory.createHttpTransport('name', credentials) >> transport()
 
         when:
-        def resolver = repository.createRealResolver()
+        def resolver = repository.createResolver()
 
         then:
         with(resolver) {
@@ -213,10 +209,10 @@ class DefaultIvyArtifactRepositoryTest extends Specification {
 
         given:
         fileResolver.resolveUri('http://host') >> new URI('http://host/')
-        transportFactory.createHttpTransport('name', credentials) >> createHttpTransport("name", credentials)
+        transportFactory.createHttpTransport('name', credentials) >> transport()
 
         when:
-        def resolver = repository.createRealResolver()
+        def resolver = repository.createResolver()
 
         then:
         with(resolver) {
@@ -240,10 +236,10 @@ class DefaultIvyArtifactRepositoryTest extends Specification {
 
         given:
         fileResolver.resolveUri('http://host') >> new URI('http://host/')
-        transportFactory.createHttpTransport('name', credentials) >> createHttpTransport("name", credentials)
+        transportFactory.createHttpTransport('name', credentials) >> transport()
 
         when:
-        def resolver = repository.createRealResolver()
+        def resolver = repository.createResolver()
 
         then:
         with(resolver) {
@@ -264,10 +260,10 @@ class DefaultIvyArtifactRepositoryTest extends Specification {
 
         given:
         fileResolver.resolveUri('http://host/') >> new URI('http://host/')
-        transportFactory.createHttpTransport('name', credentials) >> createHttpTransport("name", credentials)
+        transportFactory.createHttpTransport('name', credentials) >> transport()
 
         when:
-        def resolver = repository.createRealResolver()
+        def resolver = repository.createResolver()
 
         then:
         with(resolver) {
@@ -286,14 +282,14 @@ class DefaultIvyArtifactRepositoryTest extends Specification {
             artifact '[layoutPattern]'
         }
         repository.artifactPattern 'http://other/[additionalPattern]'
-        transportFactory.createHttpTransport('name', credentials) >> createHttpTransport("name", credentials)
+        transportFactory.createHttpTransport('name', credentials) >> transport()
 
         given:
         fileResolver.resolveUri('http://host') >> new URI('http://host')
         fileResolver.resolveUri('http://other/') >> new URI('http://other/')
 
         when:
-        def resolver = repository.createRealResolver()
+        def resolver = repository.createResolver()
 
         then:
         resolver.artifactPatterns == ['http://host/[layoutPattern]', 'http://other/[additionalPattern]']
@@ -302,17 +298,23 @@ class DefaultIvyArtifactRepositoryTest extends Specification {
 
     def "fails when no artifact patterns specified"() {
         given:
-        transportFactory.createHttpTransport('name', credentials) >> createHttpTransport("name", credentials)
+        transportFactory.createHttpTransport('name', credentials) >> transport()
 
         when:
-        repository.createRealResolver()
+        repository.createResolver()
 
         then:
         InvalidUserDataException e = thrown()
         e.message == 'You must specify a base url or at least one artifact pattern for an Ivy repository.'
     }
 
-    private HttpTransport createHttpTransport(String name, PasswordCredentials credentials) {
-        return new HttpTransport(name, credentials, cacheManager, progressLoggerFactory, Mock(TemporaryFileProvider), cachedExternalResourceIndex)
+    private RepositoryTransport transport() {
+        return Mock(RepositoryTransport) {
+            getRepository() >> resourceRepository
+            convertToPath(_) >> { URI uri ->
+                def result = uri.toString()
+                return result.endsWith('/') ? result : result + '/'
+            }
+        }
     }
 }
diff --git a/subprojects/core-impl/src/test/groovy/org/gradle/api/internal/artifacts/repositories/DefaultMavenArtifactRepositoryTest.groovy b/subprojects/core-impl/src/test/groovy/org/gradle/api/internal/artifacts/repositories/DefaultMavenArtifactRepositoryTest.groovy
index 7ce96a5..64f7b54 100644
--- a/subprojects/core-impl/src/test/groovy/org/gradle/api/internal/artifacts/repositories/DefaultMavenArtifactRepositoryTest.groovy
+++ b/subprojects/core-impl/src/test/groovy/org/gradle/api/internal/artifacts/repositories/DefaultMavenArtifactRepositoryTest.groovy
@@ -15,39 +15,39 @@
  */
 package org.gradle.api.internal.artifacts.repositories
 
-import org.apache.ivy.core.cache.RepositoryCacheManager
 import org.gradle.api.InvalidUserDataException
 import org.gradle.api.artifacts.repositories.PasswordCredentials
-import org.gradle.api.internal.artifacts.ivyservice.ivyresolve.ExternalResourceResolverAdapter
+import org.gradle.api.internal.artifacts.ModuleMetadataProcessor
+
+import org.gradle.api.internal.artifacts.ivyservice.ivyresolve.parser.MetaDataParser
 import org.gradle.api.internal.artifacts.repositories.resolver.MavenResolver
+import org.gradle.api.internal.artifacts.repositories.transport.RepositoryTransport
 import org.gradle.api.internal.artifacts.repositories.transport.RepositoryTransportFactory
-import org.gradle.api.internal.externalresource.transport.file.FileTransport
-import org.gradle.api.internal.externalresource.transport.http.HttpTransport
-import org.gradle.api.internal.file.FileResolver
-import org.gradle.api.internal.file.TemporaryFileProvider
-import spock.lang.Specification
 import org.gradle.api.internal.externalresource.local.LocallyAvailableResourceFinder
-import org.gradle.api.internal.externalresource.cached.CachedExternalResourceIndex
+import org.gradle.api.internal.externalresource.transport.ExternalResourceRepository
+import org.gradle.api.internal.file.FileResolver
 import org.gradle.logging.ProgressLoggerFactory
+import spock.lang.Specification
 
 class DefaultMavenArtifactRepositoryTest extends Specification {
     final FileResolver resolver = Mock()
     final PasswordCredentials credentials = Mock()
     final RepositoryTransportFactory transportFactory = Mock()
-    final RepositoryCacheManager cacheManager = Mock()
     final LocallyAvailableResourceFinder locallyAvailableResourceFinder = Mock()
-    final CachedExternalResourceIndex cachedExternalResourceIndex = Mock()
+    final ExternalResourceRepository resourceRepository = Mock()
+    final MetaDataParser metaDataParser = Mock()
+    final ModuleMetadataProcessor metadataProcessor = Mock()
 
-    final DefaultMavenArtifactRepository repository = new DefaultMavenArtifactRepository(resolver, credentials, transportFactory, locallyAvailableResourceFinder)
+    final DefaultMavenArtifactRepository repository = new DefaultMavenArtifactRepository(
+            resolver, credentials, transportFactory, locallyAvailableResourceFinder, metaDataParser, metadataProcessor)
     final ProgressLoggerFactory progressLoggerFactory = Mock();
 
-
     def "creates local repository"() {
         given:
         def file = new File('repo')
         def uri = file.toURI()
         _ * resolver.resolveUri('repo-dir') >> uri
-        transportFactory.createFileTransport('repo') >> new FileTransport('repo', cacheManager, Mock(TemporaryFileProvider))
+        transportFactory.createFileTransport('repo') >> transport()
 
         and:
         repository.name = 'repo'
@@ -58,7 +58,7 @@ class DefaultMavenArtifactRepositoryTest extends Specification {
 
         then:
         repo instanceof MavenResolver
-        repo.root == "${file.absolutePath}/"
+        repo.root == "${uri}/"
     }
 
     def "creates http repository"() {
@@ -67,9 +67,7 @@ class DefaultMavenArtifactRepositoryTest extends Specification {
         _ * resolver.resolveUri('repo-dir') >> uri
         _ * credentials.getUsername() >> 'username'
         _ * credentials.getPassword() >> 'password'
-        transportFactory.createHttpTransport('repo', credentials) >> createHttpTransport("repo", credentials)
-        cacheManager.name >> 'cache'
-        0 * _._
+        transportFactory.createHttpTransport('repo', credentials) >> transport()
 
         and:
         repository.name = 'repo'
@@ -88,7 +86,7 @@ class DefaultMavenArtifactRepositoryTest extends Specification {
         def file = new File('repo')
         def uri = file.toURI()
         _ * this.resolver.resolveUri('repo-dir') >> uri
-        transportFactory.createFileTransport('repo') >> new FileTransport('repo', cacheManager, Mock(TemporaryFileProvider))
+        transportFactory.createFileTransport('repo') >> transport()
 
         and:
         repository.name = 'repo'
@@ -105,8 +103,7 @@ class DefaultMavenArtifactRepositoryTest extends Specification {
         def repo = resolver.createResolver()
 
         then:
-        repo instanceof ExternalResourceResolverAdapter
-        repo.resolver.is(resolver.resolver)
+        repo.is(resolver.resolver)
     }
 
     def "creates repository with additional artifact URLs"() {
@@ -117,7 +114,7 @@ class DefaultMavenArtifactRepositoryTest extends Specification {
         _ * resolver.resolveUri('repo-dir') >> uri
         _ * resolver.resolveUri('repo1') >> uri1
         _ * resolver.resolveUri('repo2') >> uri2
-        transportFactory.createHttpTransport('repo', credentials) >> createHttpTransport("repo", credentials)
+        transportFactory.createHttpTransport('repo', credentials) >> transport()
 
         and:
         repository.name = 'repo'
@@ -136,10 +133,6 @@ class DefaultMavenArtifactRepositoryTest extends Specification {
         repo.artifactPatterns.any { it.startsWith uri2.toString() }
     }
 
-    private HttpTransport createHttpTransport(String repo, PasswordCredentials credentials) {
-        return new HttpTransport(repo, credentials, cacheManager, progressLoggerFactory, Mock(TemporaryFileProvider), cachedExternalResourceIndex)
-    }
-
     def "fails when no root url specified"() {
         when:
         repository.createLegacyDslObject()
@@ -148,4 +141,15 @@ class DefaultMavenArtifactRepositoryTest extends Specification {
         InvalidUserDataException e = thrown()
         e.message == 'You must specify a URL for a Maven repository.'
     }
+
+    private RepositoryTransport transport() {
+        return Mock(RepositoryTransport) {
+            getRepository() >> resourceRepository
+            convertToPath(_) >> { URI uri ->
+                def result = uri.toString()
+                return result.endsWith('/') ? result : result + '/'
+            }
+        }
+    }
+
 }
diff --git a/subprojects/core-impl/src/test/groovy/org/gradle/api/internal/artifacts/repositories/cachemanager/DownloadingRepositoryCacheManagerTest.groovy b/subprojects/core-impl/src/test/groovy/org/gradle/api/internal/artifacts/repositories/cachemanager/DownloadingRepositoryCacheManagerTest.groovy
deleted file mode 100644
index 61bff84..0000000
--- a/subprojects/core-impl/src/test/groovy/org/gradle/api/internal/artifacts/repositories/cachemanager/DownloadingRepositoryCacheManagerTest.groovy
+++ /dev/null
@@ -1,71 +0,0 @@
-/*
- * Copyright 2012 the original author or authors.
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- *      http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY 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.cachemanager
-
-import org.apache.ivy.core.module.descriptor.Artifact
-import org.apache.ivy.core.module.id.ArtifactRevisionId
-import org.apache.ivy.plugins.repository.Resource
-import org.apache.ivy.plugins.repository.ResourceDownloader
-import org.apache.ivy.plugins.resolver.util.ResolvedResource
-import org.gradle.api.internal.artifacts.ivyservice.CacheLockingManager
-import org.gradle.api.internal.externalresource.cached.CachedExternalResourceIndex
-import org.gradle.api.internal.file.TemporaryFileProvider
-import org.gradle.api.internal.filestore.FileStore
-import org.gradle.api.internal.filestore.FileStoreEntry
-import org.gradle.test.fixtures.file.TestNameTestDirectoryProvider
-import org.junit.Rule
-import spock.lang.Specification
-
-class DownloadingRepositoryCacheManagerTest extends Specification {
-    FileStore<ArtifactRevisionId> fileStore = Mock()
-    CachedExternalResourceIndex<String> artifactUrlCachedResolutionIndex = Mock()
-    CacheLockingManager lockingManager = Mock()
-    TemporaryFileProvider tmpFileProvider = Mock()
-    ArtifactRevisionId artifactId = Mock()
-    Artifact artifact = Mock()
-    ResourceDownloader resourceDownloader = Mock()
-    ResolvedResource artifactRef = Mock()
-    Resource resource = Mock();
-    FileStoreEntry fileStoreEntry = Mock()
-    DownloadingRepositoryCacheManager downloadingRepositoryCacheManager = new DownloadingRepositoryCacheManager("TestCacheManager", fileStore, artifactUrlCachedResolutionIndex, tmpFileProvider, lockingManager)
-
-    @Rule TestNameTestDirectoryProvider temporaryFolder;
-
-    void "downloadArtifactFile downloads artifact to temporary file and then moves it into the file store"() {
-        setup:
-
-        def downloadFile = temporaryFolder.createFile("download")
-        def storeFile = temporaryFolder.createFile("store")
-
-        _ * artifact.id >> artifactId
-        _ * artifactRef.resource >> resource
-        _ * fileStoreEntry.file >> storeFile;
-        _ * tmpFileProvider._ >> downloadFile
-
-        when:
-        downloadingRepositoryCacheManager.downloadArtifactFile(artifact, resourceDownloader, artifactRef)
-
-        then:
-        1 * lockingManager.useCache(_, _) >> {name, action ->
-            return action.create()
-        }
-        1 * resourceDownloader.download(artifact, resource, downloadFile)
-        1 * fileStore.move(artifactId, downloadFile) >> {key, action ->
-            return fileStoreEntry
-        }
-    }
-}
diff --git a/subprojects/core-impl/src/test/groovy/org/gradle/api/internal/artifacts/repositories/legacy/DownloadingRepositoryCacheManagerTest.groovy b/subprojects/core-impl/src/test/groovy/org/gradle/api/internal/artifacts/repositories/legacy/DownloadingRepositoryCacheManagerTest.groovy
new file mode 100644
index 0000000..23c89c4
--- /dev/null
+++ b/subprojects/core-impl/src/test/groovy/org/gradle/api/internal/artifacts/repositories/legacy/DownloadingRepositoryCacheManagerTest.groovy
@@ -0,0 +1,66 @@
+/*
+ * Copyright 2013 the original author or authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY 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.legacy
+
+import org.apache.ivy.core.module.descriptor.Artifact
+import org.apache.ivy.core.module.id.ArtifactRevisionId
+import org.apache.ivy.plugins.repository.Resource
+import org.apache.ivy.plugins.repository.ResourceDownloader
+import org.gradle.api.internal.artifacts.ivyservice.CacheLockingManager
+import org.gradle.api.internal.file.TemporaryFileProvider
+import org.gradle.internal.filestore.FileStore
+import org.gradle.internal.resource.local.LocallyAvailableResource
+import org.gradle.test.fixtures.file.TestNameTestDirectoryProvider
+import org.junit.Rule
+import spock.lang.Specification
+
+class DownloadingRepositoryCacheManagerTest extends Specification {
+    FileStore<ArtifactRevisionId> fileStore = Mock()
+    CacheLockingManager lockingManager = Mock()
+    TemporaryFileProvider tmpFileProvider = Mock()
+    ArtifactRevisionId artifactId = Mock()
+    Artifact artifact = Mock()
+    ResourceDownloader resourceDownloader = Mock()
+    Resource resource = Mock();
+    LocallyAvailableResource fileStoreEntry = Mock()
+    DownloadingRepositoryCacheManager downloadingRepositoryCacheManager = new DownloadingRepositoryCacheManager("TestCacheManager", fileStore, tmpFileProvider, lockingManager)
+
+    @Rule TestNameTestDirectoryProvider temporaryFolder;
+
+    void "downloads artifact to temporary file and then moves it into the file store"() {
+        setup:
+
+        def downloadFile = temporaryFolder.createFile("download")
+        def storeFile = temporaryFolder.createFile("store")
+
+        _ * artifact.id >> artifactId
+        _ * fileStoreEntry.file >> storeFile;
+        _ * tmpFileProvider._ >> downloadFile
+
+        when:
+        downloadingRepositoryCacheManager.downloadAndCacheArtifactFile(artifact, resourceDownloader, resource)
+
+        then:
+        1 * lockingManager.useCache(_, _) >> {name, action ->
+            return action.create()
+        }
+        1 * resourceDownloader.download(artifact, resource, downloadFile)
+        1 * fileStore.move(artifactId, downloadFile) >> {key, action ->
+            return fileStoreEntry
+        }
+    }
+}
diff --git a/subprojects/core-impl/src/test/groovy/org/gradle/api/internal/artifacts/repositories/resolver/ChainedVersionListerTest.groovy b/subprojects/core-impl/src/test/groovy/org/gradle/api/internal/artifacts/repositories/resolver/ChainedVersionListerTest.groovy
index 2cac12d..349f432 100644
--- a/subprojects/core-impl/src/test/groovy/org/gradle/api/internal/artifacts/repositories/resolver/ChainedVersionListerTest.groovy
+++ b/subprojects/core-impl/src/test/groovy/org/gradle/api/internal/artifacts/repositories/resolver/ChainedVersionListerTest.groovy
@@ -17,7 +17,7 @@
 package org.gradle.api.internal.artifacts.repositories.resolver
 
 import org.apache.ivy.core.module.descriptor.Artifact
-import org.apache.ivy.core.module.id.ModuleRevisionId
+import org.gradle.api.artifacts.ModuleVersionSelector
 import org.gradle.api.internal.resource.ResourceNotFoundException
 import spock.lang.Specification
 import spock.lang.Unroll
@@ -33,17 +33,17 @@ class ChainedVersionListerTest extends Specification {
 
     ResourcePattern pattern = Mock()
     Artifact artifact = Mock()
-    ModuleRevisionId moduleRevisionId = Mock()
+    ModuleVersionSelector selector = Mock()
 
-    def chainedVersionLister = new org.gradle.api.internal.artifacts.repositories.resolver.ChainedVersionLister(lister1, lister2)
+    def chainedVersionLister = new ChainedVersionLister(lister1, lister2)
 
     def "visit stops listing after first success"() {
         when:
-        VersionList versionList = chainedVersionLister.getVersionList(moduleRevisionId);
+        VersionList versionList = chainedVersionLister.getVersionList(selector);
 
         then:
-        1 * lister1.getVersionList(moduleRevisionId) >> versionList1
-        1 * lister2.getVersionList(moduleRevisionId) >> versionList2
+        1 * lister1.getVersionList(selector) >> versionList1
+        1 * lister2.getVersionList(selector) >> versionList2
 
         when:
         versionList.visit(pattern, artifact)
@@ -53,23 +53,23 @@ class ChainedVersionListerTest extends Specification {
         0 * _._
 
         when:
-        def result = versionList.versionStrings
+        def result = versionList.versions
 
         then:
         result == ["1.0", "1.2"] as Set
 
         and:
-        versionList1.versionStrings >> ["1.0", "1.2"]
-        versionList2.versionStrings >> []
+        versionList1.versions >> ["1.0", "1.2"]
+        versionList2.versions >> []
     }
 
     @Unroll
     def "visit ignores #exception.class.simpleName of failed VersionLister"() {
         given:
-        lister1.getVersionList(moduleRevisionId) >> versionList1
-        lister2.getVersionList(moduleRevisionId) >> versionList2
+        lister1.getVersionList(selector) >> versionList1
+        lister2.getVersionList(selector) >> versionList2
 
-        VersionList versionList = chainedVersionLister.getVersionList(moduleRevisionId)
+        VersionList versionList = chainedVersionLister.getVersionList(selector)
 
         when:
         versionList.visit(pattern, artifact)
@@ -85,10 +85,10 @@ class ChainedVersionListerTest extends Specification {
     def "visit rethrows ResourceNotFoundException of failed last VersionLister"() {
         given:
         def exception = new ResourceNotFoundException("not found")
-        lister1.getVersionList(moduleRevisionId) >> versionList1
-        lister2.getVersionList(moduleRevisionId) >> versionList2
+        lister1.getVersionList(selector) >> versionList1
+        lister2.getVersionList(selector) >> versionList2
 
-        VersionList versionList = chainedVersionLister.getVersionList(moduleRevisionId)
+        VersionList versionList = chainedVersionLister.getVersionList(selector)
 
         when:
         versionList.visit(pattern, artifact)
@@ -105,17 +105,17 @@ class ChainedVersionListerTest extends Specification {
     def "visit wraps failed last VersionLister"() {
         given:
         def exception = new RuntimeException("broken")
-        lister1.getVersionList(moduleRevisionId) >> versionList1
-        lister2.getVersionList(moduleRevisionId) >> versionList2
+        lister1.getVersionList(selector) >> versionList1
+        lister2.getVersionList(selector) >> versionList2
 
-        VersionList versionList = chainedVersionLister.getVersionList(moduleRevisionId)
+        VersionList versionList = chainedVersionLister.getVersionList(selector)
 
         when:
         versionList.visit(pattern, artifact)
 
         then:
         def e = thrown(ResourceException)
-        e.message == "Failed to list versions for ${moduleRevisionId}."
+        e.message == "Failed to list versions for ${selector}."
         e.cause == exception
 
         and:
diff --git a/subprojects/core-impl/src/test/groovy/org/gradle/api/internal/artifacts/repositories/resolver/ExternalResourceResolverTest.groovy b/subprojects/core-impl/src/test/groovy/org/gradle/api/internal/artifacts/repositories/resolver/ExternalResourceResolverTest.groovy
index b2211cf..1f4b9a5 100644
--- a/subprojects/core-impl/src/test/groovy/org/gradle/api/internal/artifacts/repositories/resolver/ExternalResourceResolverTest.groovy
+++ b/subprojects/core-impl/src/test/groovy/org/gradle/api/internal/artifacts/repositories/resolver/ExternalResourceResolverTest.groovy
@@ -15,15 +15,14 @@
  */
 
 package org.gradle.api.internal.artifacts.repositories.resolver
-
-import org.apache.ivy.core.module.descriptor.Artifact
 import org.apache.ivy.core.module.id.ArtifactRevisionId
-import org.apache.ivy.core.module.id.ModuleId
-import org.apache.ivy.core.module.id.ModuleRevisionId
-import org.apache.ivy.core.report.ArtifactDownloadReport
-import org.apache.ivy.core.report.DownloadStatus
+import org.gradle.api.artifacts.ArtifactIdentifier
+import org.gradle.api.internal.artifacts.DefaultArtifactIdentifier
+import org.gradle.api.internal.artifacts.DefaultModuleVersionIdentifier
+import org.gradle.api.internal.artifacts.ModuleMetadataProcessor
 import org.gradle.api.internal.artifacts.ivyservice.BuildableArtifactResolveResult
-import org.gradle.api.internal.artifacts.repositories.cachemanager.EnhancedArtifactDownloadReport
+import org.gradle.api.internal.artifacts.ivyservice.ivyresolve.ArtifactResolveException
+import org.gradle.api.internal.artifacts.ivyservice.ivyresolve.parser.MetaDataParser
 import org.gradle.api.internal.externalresource.local.LocallyAvailableResourceFinder
 import org.gradle.api.internal.externalresource.transport.ExternalResourceRepository
 import spock.lang.Specification
@@ -34,17 +33,16 @@ class ExternalResourceResolverTest extends Specification {
     VersionLister versionLister = Mock()
     LocallyAvailableResourceFinder<ArtifactRevisionId> locallyAvailableResourceFinder = Mock()
     BuildableArtifactResolveResult result = Mock()
-    Artifact artifact = Mock()
+    MetaDataParser parser = Mock()
+    ModuleMetadataProcessor metadataProcessor = Mock()
+    ArtifactIdentifier artifact = new DefaultArtifactIdentifier(DefaultModuleVersionIdentifier.newId("group", "module", "version"), "name", "type", "ext", "classifier")
     MavenResolver.TimestampedModuleSource moduleSource = Mock()
-    EnhancedArtifactDownloadReport downloadReport = Mock()
-
+    File downloadedFile = Mock(File)
     ExternalResourceResolver resolver
-    ModuleRevisionId artifactModuleRevisionId = Mock()
 
     def setup() {
         //We use a spy here to avoid dealing with all the overhead ivys basicresolver brings in here.
-        resolver = Spy(ExternalResourceResolver, constructorArgs: [name, repository, versionLister, locallyAvailableResourceFinder])
-        resolver.download(_) >> downloadReport
+        resolver = Spy(ExternalResourceResolver, constructorArgs: [name, repository, versionLister, locallyAvailableResourceFinder, parser, metadataProcessor])
     }
 
     def reportsNotFoundArtifactResolveResult() {
@@ -61,14 +59,15 @@ class ExternalResourceResolverTest extends Specification {
 
     def reportsFailedArtifactResolveResult() {
         given:
-        downloadIsFailing()
+        downloadIsFailing(new IOException("DOWNLOAD FAILURE"))
 
         when:
         resolver.resolve(artifact, result, moduleSource)
 
         then:
-        1 * result.failed(_) >> { exception ->
-            assert exception.message.toString() == "[Could not download artifact 'group:projectA:1.0 at jar']"
+        1 * result.failed(_) >> { ArtifactResolveException exception ->
+            assert exception.message == "Could not download artifact 'group:module:version:classifier at ext'"
+            assert exception.cause.message == "DOWNLOAD FAILURE"
         }
         0 * result._
     }
@@ -100,39 +99,19 @@ class ExternalResourceResolverTest extends Specification {
 
     def artifactIsTimestampedSnapshotVersion() {
         _ * moduleSource.timestampedVersion >> "1.0-20100101.120001-1"
-        _ * artifact.getModuleRevisionId() >> artifactModuleRevisionId
-        _ * artifactModuleRevisionId.organisation >> "group"
-        _ * artifactModuleRevisionId.name >> "projectA"
-        _ * artifactModuleRevisionId.revision >> "1.0"
-        ModuleId moduleId = Mock()
-        _ * moduleId.equals(_) >> true
-        _ * artifactModuleRevisionId.moduleId >> moduleId
-        _ * artifactModuleRevisionId.qualifiedExtraAttributes >> [:]
     }
 
     def artifactIsMissing() {
-        1 * downloadReport.getDownloadStatus() >> DownloadStatus.FAILED
-        1 * downloadReport.getDownloadDetails() >> ArtifactDownloadReport.MISSING_ARTIFACT;
+        resolver.download(_) >> null
     }
 
-    def downloadIsFailing() {
-        1 * downloadReport.getDownloadStatus() >> DownloadStatus.FAILED
-        1 * downloadReport.getDownloadDetails() >> "Broken Connection";
-        1 * downloadReport.getArtifact() >> artifact
-        1 * artifact.getModuleRevisionId() >> {
-            ModuleRevisionId moduleRevisionId = Mock()
-            1 * moduleRevisionId.organisation >> "group"
-            1 * moduleRevisionId.name >> "projectA"
-            1 * moduleRevisionId.revision >> "1.0"
-            1 * artifact.ext >> "jar"
-            moduleRevisionId
-        };
+    def downloadIsFailing(IOException failure) {
+        resolver.download(_) >> {
+            throw failure
+        }
     }
 
-
     def artifactCanBeResolved() {
-        1 * downloadReport.getDownloadStatus() >> DownloadStatus.SUCCESSFUL
-        1 * downloadReport.getLocalFile() >> Mock(File);
-
+        resolver.download(_) >> downloadedFile
     }
 }
diff --git a/subprojects/core-impl/src/test/groovy/org/gradle/api/internal/artifacts/repositories/resolver/MavenResolverTest.groovy b/subprojects/core-impl/src/test/groovy/org/gradle/api/internal/artifacts/repositories/resolver/MavenResolverTest.groovy
index bde5c11..e19952a 100644
--- a/subprojects/core-impl/src/test/groovy/org/gradle/api/internal/artifacts/repositories/resolver/MavenResolverTest.groovy
+++ b/subprojects/core-impl/src/test/groovy/org/gradle/api/internal/artifacts/repositories/resolver/MavenResolverTest.groovy
@@ -16,6 +16,8 @@
 
 package org.gradle.api.internal.artifacts.repositories.resolver
 
+import org.gradle.api.internal.artifacts.ModuleMetadataProcessor
+import org.gradle.api.internal.artifacts.ivyservice.ivyresolve.parser.MetaDataParser
 import org.gradle.api.internal.artifacts.repositories.transport.RepositoryTransport
 import org.gradle.api.internal.externalresource.local.LocallyAvailableResourceFinder
 import org.gradle.api.internal.externalresource.transport.ExternalResourceRepository
@@ -28,6 +30,8 @@ class MavenResolverTest extends Specification {
 
     def rootUri = URI.create("localhost:8081:/testrepo/")
     def locallyAvailableResourceFinder = Mock(LocallyAvailableResourceFinder)
+    def parser = Mock(MetaDataParser)
+    def processor = Mock(ModuleMetadataProcessor)
 
     def setup() {
         repositoryTransport.getRepository() >> repository
@@ -36,7 +40,7 @@ class MavenResolverTest extends Specification {
     @Unroll
     def "setUseMavenMetaData '#value' adapts versionLister to #classname"() {
         setup:
-        MavenResolver testresolver = new MavenResolver("test maven resolver", rootUri, repositoryTransport, locallyAvailableResourceFinder)
+        MavenResolver testresolver = new MavenResolver("test maven resolver", rootUri, repositoryTransport, locallyAvailableResourceFinder, parser, processor)
         when:
         testresolver.setUseMavenMetadata(value)
         then:
diff --git a/subprojects/core-impl/src/test/groovy/org/gradle/api/internal/artifacts/repositories/resolver/MavenVersionListerTest.groovy b/subprojects/core-impl/src/test/groovy/org/gradle/api/internal/artifacts/repositories/resolver/MavenVersionListerTest.groovy
index 1538348..03293fb 100644
--- a/subprojects/core-impl/src/test/groovy/org/gradle/api/internal/artifacts/repositories/resolver/MavenVersionListerTest.groovy
+++ b/subprojects/core-impl/src/test/groovy/org/gradle/api/internal/artifacts/repositories/resolver/MavenVersionListerTest.groovy
@@ -18,37 +18,43 @@ package org.gradle.api.internal.artifacts.repositories.resolver
 
 import org.apache.ivy.core.module.descriptor.DefaultArtifact
 import org.apache.ivy.core.module.id.ModuleRevisionId
+import org.gradle.api.Action
+import org.gradle.api.internal.artifacts.DefaultModuleVersionSelector
+import org.gradle.api.internal.artifacts.ivyservice.ivyresolve.strategy.ExactVersionMatcher
+import org.gradle.api.internal.artifacts.ivyservice.ivyresolve.strategy.LatestVersionStrategy
 import org.gradle.api.internal.externalresource.ExternalResource
 import org.gradle.api.internal.externalresource.transport.ExternalResourceRepository
 import org.gradle.api.internal.resource.ResourceException
 import org.gradle.api.internal.resource.ResourceNotFoundException
+import org.gradle.internal.UncheckedException
 import org.xml.sax.SAXParseException
 import spock.lang.Specification
 
 class MavenVersionListerTest extends Specification {
     def repo = Mock(ExternalResourceRepository)
     def moduleRevisionId = ModuleRevisionId.newInstance("org.acme", "testproject", "1.0")
+    def selector = DefaultModuleVersionSelector.newSelector(moduleRevisionId)
     def artifact = new DefaultArtifact(moduleRevisionId, new Date(), "testproject", "jar", "jar")
 
     def repository = Mock(ExternalResourceRepository)
     def pattern = pattern("localhost:8081/testRepo/" + MavenPattern.M2_PATTERN)
     String metaDataResource = 'localhost:8081/testRepo/org/acme/testproject/maven-metadata.xml'
 
-    final org.gradle.api.internal.artifacts.repositories.resolver.MavenVersionLister lister = new org.gradle.api.internal.artifacts.repositories.resolver.MavenVersionLister(repository)
+    final MavenVersionLister lister = new MavenVersionLister(repository)
 
     def "visit parses maven-metadata.xml"() {
         ExternalResource resource = Mock()
 
         when:
-        def versionList = lister.getVersionList(moduleRevisionId)
+        def versionList = lister.getVersionList(selector)
         versionList.visit(pattern, artifact)
 
         then:
-        versionList.versionStrings == ['1.1', '1.2'] as Set
+        sort(versionList).collect {it.version} == ['1.2', '1.1']
 
         and:
         1 * repository.getResource(metaDataResource) >> resource
-        1 * resource.openStream() >> new ByteArrayInputStream("""
+        1 * resource.withContent(_) >> { Action action -> action.execute(new ByteArrayInputStream("""
 <metadata>
     <versioning>
         <versions>
@@ -56,7 +62,8 @@ class MavenVersionListerTest extends Specification {
             <version>1.2</version>
         </versions>
     </versioning>
-</metadata>""".bytes)
+</metadata>""".bytes))
+        }
         1 * resource.close()
         0 * repository._
         0 * resource._
@@ -67,16 +74,19 @@ class MavenVersionListerTest extends Specification {
         ExternalResource resource2 = Mock()
 
         when:
-        def versionList = lister.getVersionList(moduleRevisionId)
-        versionList.visit(pattern("prefix1/" + MavenPattern.M2_PATTERN), artifact)
-        versionList.visit(pattern("prefix2/" + MavenPattern.M2_PATTERN), artifact)
+        def versionList = lister.getVersionList(selector)
+        final pattern1 = pattern("prefix1/" + MavenPattern.M2_PATTERN)
+        versionList.visit(pattern1, artifact)
+        final pattern2 = pattern("prefix2/" + MavenPattern.M2_PATTERN)
+        versionList.visit(pattern2, artifact)
 
         then:
-        versionList.versionStrings == ['1.1', '1.2', '1.3'] as Set
+        sort(versionList).collect {it.version} == ['1.3', '1.2', '1.1']
+        sort(versionList).collect {it.pattern} == [pattern2, pattern1, pattern1]
 
         and:
         1 * repository.getResource('prefix1/org/acme/testproject/maven-metadata.xml') >> resource1
-        1 * resource1.openStream() >> new ByteArrayInputStream("""
+        1 * resource1.withContent(_) >> { Action action -> action.execute(new ByteArrayInputStream("""
 <metadata>
     <versioning>
         <versions>
@@ -84,9 +94,10 @@ class MavenVersionListerTest extends Specification {
             <version>1.2</version>
         </versions>
     </versioning>
-</metadata>""".bytes)
+</metadata>""".bytes))
+        }
         1 * repository.getResource('prefix2/org/acme/testproject/maven-metadata.xml') >> resource2
-        1 * resource2.openStream() >> new ByteArrayInputStream("""
+        1 * resource2.withContent(_) >> { Action action -> action.execute(new ByteArrayInputStream("""
 <metadata>
     <versioning>
         <versions>
@@ -94,23 +105,24 @@ class MavenVersionListerTest extends Specification {
             <version>1.3</version>
         </versions>
     </versioning>
-</metadata>""".bytes)
+</metadata>""".bytes))
+        }
     }
 
     def "visit ignores duplicate patterns"() {
         ExternalResource resource = Mock()
 
         when:
-        def versionList = lister.getVersionList(moduleRevisionId)
+        def versionList = lister.getVersionList(selector)
         versionList.visit(pattern, artifact)
         versionList.visit(pattern, artifact)
 
         then:
-        versionList.versionStrings == ['1.1', '1.2'] as Set
+        sort(versionList).collect {it.version} == ['1.2', '1.1']
 
         and:
         1 * repository.getResource(metaDataResource) >> resource
-        1 * resource.openStream() >> new ByteArrayInputStream("""
+        1 * resource.withContent(_) >> { Action action -> action.execute(new ByteArrayInputStream("""
 <metadata>
     <versioning>
         <versions>
@@ -118,15 +130,21 @@ class MavenVersionListerTest extends Specification {
             <version>1.2</version>
         </versions>
     </versioning>
-</metadata>""".bytes)
+</metadata>""".bytes))
+        }
         1 * resource.close()
         0 * repository._
         0 * resource._
     }
 
+    private static List<VersionList.ListedVersion> sort(VersionList versionList) {
+        def latestStrategy = new LatestVersionStrategy(new ExactVersionMatcher())
+        versionList.sortLatestFirst(latestStrategy)
+    }
+
     def "visit throws ResourceNotFoundException when maven-metadata not available"() {
         when:
-        def versionList = lister.getVersionList(moduleRevisionId)
+        def versionList = lister.getVersionList(selector)
         versionList.visit(pattern, artifact)
 
         then:
@@ -140,16 +158,17 @@ class MavenVersionListerTest extends Specification {
         ExternalResource resource = Mock()
 
         when:
-        def versionList = lister.getVersionList(moduleRevisionId)
+        def versionList = lister.getVersionList(selector)
         versionList.visit(pattern, artifact)
 
         then:
         ResourceException e = thrown()
         e.message == "Unable to load Maven meta-data from $metaDataResource."
-        e.cause instanceof SAXParseException
+        e.cause instanceof UncheckedException
+        e.cause.cause instanceof SAXParseException
         1 * resource.close()
         1 * repository.getResource(metaDataResource) >> resource;
-        1 * resource.openStream() >> new ByteArrayInputStream("yo".bytes)
+        1 * resource.withContent(_) >> { Action action -> action.execute(new ByteArrayInputStream("yo".bytes)) }
         0 * repository._
     }
 
@@ -157,7 +176,7 @@ class MavenVersionListerTest extends Specification {
         def failure = new IOException()
 
         when:
-        def versionList = lister.getVersionList(moduleRevisionId)
+        def versionList = lister.getVersionList(selector)
         versionList.visit(pattern, artifact)
 
         then:
@@ -169,6 +188,6 @@ class MavenVersionListerTest extends Specification {
     }
 
     def pattern(String pattern) {
-        return new org.gradle.api.internal.artifacts.repositories.resolver.M2ResourcePattern(pattern)
+        return new M2ResourcePattern(pattern)
     }
 }
diff --git a/subprojects/core-impl/src/test/groovy/org/gradle/api/internal/artifacts/repositories/resolver/ResourceVersionListerTest.groovy b/subprojects/core-impl/src/test/groovy/org/gradle/api/internal/artifacts/repositories/resolver/ResourceVersionListerTest.groovy
index 42128df..92f0b39 100644
--- a/subprojects/core-impl/src/test/groovy/org/gradle/api/internal/artifacts/repositories/resolver/ResourceVersionListerTest.groovy
+++ b/subprojects/core-impl/src/test/groovy/org/gradle/api/internal/artifacts/repositories/resolver/ResourceVersionListerTest.groovy
@@ -15,9 +15,11 @@
  */
 
 package org.gradle.api.internal.artifacts.repositories.resolver
-
 import org.apache.ivy.core.module.descriptor.DefaultArtifact
 import org.apache.ivy.core.module.id.ModuleRevisionId
+import org.gradle.api.internal.artifacts.DefaultModuleVersionSelector
+import org.gradle.api.internal.artifacts.ivyservice.ivyresolve.strategy.ExactVersionMatcher
+import org.gradle.api.internal.artifacts.ivyservice.ivyresolve.strategy.LatestVersionStrategy
 import org.gradle.api.internal.externalresource.transport.ExternalResourceRepository
 import org.gradle.api.internal.resource.ResourceException
 import org.gradle.api.internal.resource.ResourceNotFoundException
@@ -28,12 +30,13 @@ class ResourceVersionListerTest extends Specification {
 
     def repo = Mock(ExternalResourceRepository)
     def moduleRevisionId = ModuleRevisionId.newInstance("org.acme", "proj1", "1.0")
+    def selector = DefaultModuleVersionSelector.newSelector(moduleRevisionId)
     def artifact = new DefaultArtifact(moduleRevisionId, new Date(), "proj1", "jar", "jar")
 
-    def org.gradle.api.internal.artifacts.repositories.resolver.ResourceVersionLister lister;
+    def ResourceVersionLister lister;
 
     def setup() {
-        lister = new org.gradle.api.internal.artifacts.repositories.resolver.ResourceVersionLister(repo)
+        lister = new ResourceVersionLister(repo)
     }
 
     def "visit propagates Exceptions as ResourceException"() {
@@ -43,7 +46,7 @@ class ResourceVersionListerTest extends Specification {
         1 * repo.list(_) >> { throw failure }
 
         when:
-        def versionList = lister.getVersionList(moduleRevisionId)
+        def versionList = lister.getVersionList(selector)
         versionList.visit(testPattern, artifact)
 
         then:
@@ -57,7 +60,7 @@ class ResourceVersionListerTest extends Specification {
         1 * repo.list(_) >> null
 
         when:
-        def versionList = lister.getVersionList(moduleRevisionId)
+        def versionList = lister.getVersionList(selector)
         versionList.visit(pattern(testPattern), artifact)
 
         then:
@@ -73,7 +76,7 @@ class ResourceVersionListerTest extends Specification {
         1 * repo.list(_) >> []
 
         when:
-        def versionList = lister.getVersionList(moduleRevisionId)
+        def versionList = lister.getVersionList(selector)
         versionList.visit(pattern("/some/[revision]"), artifact)
 
         then:
@@ -83,11 +86,11 @@ class ResourceVersionListerTest extends Specification {
     @Unroll
     def "visit resolves versions from from pattern with '#testPattern'"() {
         when:
-        def versionList = lister.getVersionList(moduleRevisionId)
+        def versionList = lister.getVersionList(selector)
         versionList.visit(pattern(testPattern), artifact)
 
         then:
-        versionList.versionStrings == ["1", "2.1", "a-version"] as Set
+        sort(versionList).collect { it.version } == ["2.1", "1", "a-version"]
 
         and:
         1 * repo.list(repoListingPath) >> repoResult
@@ -116,12 +119,15 @@ class ResourceVersionListerTest extends Specification {
 
     def "visit builds union of versions"() {
         when:
-        def versionList = lister.getVersionList(moduleRevisionId)
-        versionList.visit(pattern("/[revision]/[artifact]-[revision].[ext]"), artifact)
-        versionList.visit(pattern("/[organisation]/[revision]/[artifact]-[revision].[ext]"), artifact)
+        def versionList = lister.getVersionList(selector)
+        def pattern1 = pattern("/[revision]/[artifact]-[revision].[ext]")
+        def pattern2 = pattern("/[organisation]/[revision]/[artifact]-[revision].[ext]")
+        versionList.visit(pattern1, artifact)
+        versionList.visit(pattern2, artifact)
 
         then:
-        versionList.versionStrings == ["1.2", "1.3", "1.4"] as Set
+        sort(versionList).collect { it.version } == ["1.4", "1.3", "1.2"]
+        sort(versionList).collect { it.pattern } == [pattern2, pattern1, pattern1]
 
         and:
         1 * repo.list("/") >> ["1.2", "1.3"]
@@ -131,21 +137,28 @@ class ResourceVersionListerTest extends Specification {
 
     def "visit ignores duplicate patterns"() {
         when:
-        def versionList = lister.getVersionList(moduleRevisionId)
-        versionList.visit(pattern("/a/[revision]/[artifact]-[revision].[ext]"), artifact)
+        def versionList = lister.getVersionList(selector)
+        final patternA = pattern("/a/[revision]/[artifact]-[revision].[ext]")
+        versionList.visit(patternA, artifact)
         versionList.visit(pattern("/a/[revision]/[artifact]-[revision]"), artifact)
 
         then:
-        versionList.versionStrings == ["1.2", "1.3"] as Set
+        sort(versionList).collect { it.version } == ["1.3", "1.2"]
+        sort(versionList).collect { it.pattern } == [patternA, patternA]
 
         and:
         1 * repo.list("/a/") >> ["1.2", "1.3"]
         0 * repo._
     }
 
+    private static List<VersionList.ListedVersion> sort(VersionList versionList) {
+        def latestStrategy = new LatestVersionStrategy(new ExactVersionMatcher())
+        versionList.sortLatestFirst(latestStrategy)
+    }
+
     def "visit substitutes non revision placeholders from pattern before hitting repository"() {
         when:
-        def versionList = lister.getVersionList(moduleRevisionId)
+        def versionList = lister.getVersionList(selector)
         versionList.visit(pattern(inputPattern), artifact)
 
         then:
@@ -167,7 +180,7 @@ class ResourceVersionListerTest extends Specification {
         repo.list(_) >> repoResult
 
         when:
-        def versionList = lister.getVersionList(moduleRevisionId)
+        def versionList = lister.getVersionList(selector)
         versionList.visit(pattern(testPattern), artifact)
 
         then:
diff --git a/subprojects/core-impl/src/test/groovy/org/gradle/api/internal/artifacts/resolutioncache/DefaultArtifactResolutionCacheTest.groovy b/subprojects/core-impl/src/test/groovy/org/gradle/api/internal/artifacts/resolutioncache/DefaultArtifactResolutionCacheTest.groovy
deleted file mode 100644
index f352560..0000000
--- a/subprojects/core-impl/src/test/groovy/org/gradle/api/internal/artifacts/resolutioncache/DefaultArtifactResolutionCacheTest.groovy
+++ /dev/null
@@ -1,80 +0,0 @@
-/*
- * Copyright 2012 the original author or authors.
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- *      http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY 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.resolutioncache
-
-import org.gradle.CacheUsage
-import org.gradle.api.internal.artifacts.ivyservice.DefaultCacheLockingManager
-import org.gradle.api.internal.externalresource.cached.DefaultCachedExternalResourceIndex
-import org.gradle.api.internal.externalresource.metadata.DefaultExternalResourceMetaData
-import org.gradle.cache.internal.CacheFactory
-import org.gradle.cache.internal.DefaultCacheRepository
-import org.gradle.internal.TimeProvider
-import org.gradle.test.fixtures.file.TestNameTestDirectoryProvider
-import org.gradle.testfixtures.internal.InMemoryCacheFactory
-import org.junit.Rule
-import spock.lang.Specification
-import spock.lang.Unroll
-
-class DefaultArtifactResolutionCacheTest extends Specification {
-
-    @Rule TestNameTestDirectoryProvider tmp = new TestNameTestDirectoryProvider()
-
-    Date time = new Date(0)
-    TimeProvider timeProvider = new TimeProvider() {
-        long getCurrentTime() { time.getTime() }
-    }
-
-    CacheFactory cacheFactory = new InMemoryCacheFactory()
-    DefaultCacheRepository cacheRepository
-    DefaultCacheLockingManager cacheLockingManager
-    
-    DefaultCachedExternalResourceIndex<String> index
-
-    def setup() {
-        cacheRepository = new DefaultCacheRepository(tmp.createDir('user-home'), tmp.createDir('project-cache'), CacheUsage.ON, cacheFactory)
-        cacheLockingManager = new DefaultCacheLockingManager(cacheRepository)
-        index = new DefaultCachedExternalResourceIndex(tmp.createFile("index"), String, timeProvider, cacheLockingManager)
-    }
-
-    @Unroll "stores entry - lastModified = #lastModified, artifactUrl = #artifactUrl"() {
-        given:
-        def key = "key"
-        def artifactFile = tmp.createFile("artifact") << "content"
-        
-        when:
-        index.store(key, artifactFile, new DefaultExternalResourceMetaData(artifactUrl, lastModified, 100, null, null))
-        
-        then:
-        def cached = index.lookup(key)
-        
-        and:
-        cached != null
-        cached.cachedFile == artifactFile
-        cached.externalResourceMetaData != null
-        cached.externalResourceMetaData.lastModified == lastModified
-        cached.externalResourceMetaData.location == artifactUrl
-
-
-        where:
-        lastModified | artifactUrl
-        new Date()   | null
-        null         | "abc"
-        null         | null
-        new Date()   | "abc"
-    }
-
-}
diff --git a/subprojects/core-impl/src/test/groovy/org/gradle/api/internal/artifacts/result/DefaultResolutionResultTest.groovy b/subprojects/core-impl/src/test/groovy/org/gradle/api/internal/artifacts/result/DefaultResolutionResultTest.groovy
index 919106c..6f71085 100644
--- a/subprojects/core-impl/src/test/groovy/org/gradle/api/internal/artifacts/result/DefaultResolutionResultTest.groovy
+++ b/subprojects/core-impl/src/test/groovy/org/gradle/api/internal/artifacts/result/DefaultResolutionResultTest.groovy
@@ -19,11 +19,9 @@ package org.gradle.api.internal.artifacts.result
 import spock.lang.Specification
 
 import static org.gradle.api.internal.artifacts.DefaultModuleVersionSelector.newSelector
+import org.gradle.internal.Factory;
 import static org.gradle.api.internal.artifacts.result.ResolutionResultDataBuilder.*
 
-/**
- * by Szczepan Faber, created at: 9/20/12
- */
 class DefaultResolutionResultTest extends Specification {
 
     def "provides all modules and dependencies including unresolved"() {
@@ -39,8 +37,8 @@ class DefaultResolutionResultTest extends Specification {
         dep2.selected.addDependency(dep3).addDependency(dep4)
 
         when:
-        def deps = new DefaultResolutionResult(root).allDependencies
-        def modules = new DefaultResolutionResult(root).allModuleVersions
+        def deps = new DefaultResolutionResult({root} as Factory).allDependencies
+        def modules = new DefaultResolutionResult({root} as Factory).allModuleVersions
 
         then:
         deps == [dep1, dep2, dep3, dep4] as Set
@@ -58,7 +56,7 @@ class DefaultResolutionResultTest extends Specification {
         def root = newModule('root').addDependency(dep).addDependency(newDependency('dep2')).addDependency(dep3)
         dep.selected.addDependency(dep3)
 
-        def result = new DefaultResolutionResult(root)
+        def result = new DefaultResolutionResult({root} as Factory)
 
         when:
         def deps = []
@@ -82,8 +80,8 @@ class DefaultResolutionResultTest extends Specification {
         dep1.selected.addDependency(new DefaultResolvedDependencyResult(newSelector('a', 'a', '1'), root, dep1.selected))
 
         when:
-        def deps = new DefaultResolutionResult(root).allDependencies
-        def modules = new DefaultResolutionResult(root).allModuleVersions
+        def deps = new DefaultResolutionResult({root} as Factory).allDependencies
+        def modules = new DefaultResolutionResult({root} as Factory).allModuleVersions
 
         then:
         deps.size() == 2
@@ -98,7 +96,7 @@ class DefaultResolutionResultTest extends Specification {
         def root = newModule('root').addDependency(dep1).addDependency(dep2)
 
         when:
-        def result = new DefaultResolutionResult(root)
+        def result = new DefaultResolutionResult({root} as Factory)
 
         then:
         result.allDependencies == [dep1, dep2] as Set
diff --git a/subprojects/core-impl/src/test/groovy/org/gradle/api/internal/artifacts/result/DefaultResolvedModuleVersionResultSpec.groovy b/subprojects/core-impl/src/test/groovy/org/gradle/api/internal/artifacts/result/DefaultResolvedModuleVersionResultSpec.groovy
index 127622c..e19bc8c 100644
--- a/subprojects/core-impl/src/test/groovy/org/gradle/api/internal/artifacts/result/DefaultResolvedModuleVersionResultSpec.groovy
+++ b/subprojects/core-impl/src/test/groovy/org/gradle/api/internal/artifacts/result/DefaultResolvedModuleVersionResultSpec.groovy
@@ -20,10 +20,6 @@ import spock.lang.Specification
 
 import static org.gradle.api.internal.artifacts.result.ResolutionResultDataBuilder.*
 
-/**
- * Created: 10/08/2012
- * @author Szczepan Faber
- */
 class DefaultResolvedModuleVersionResultSpec extends Specification {
 
     def "mutating dependencies or dependents is harmless"() {
diff --git a/subprojects/core-impl/src/test/groovy/org/gradle/api/internal/externalresource/CachedExternalResourceAdapterTest.groovy b/subprojects/core-impl/src/test/groovy/org/gradle/api/internal/externalresource/CachedExternalResourceAdapterTest.groovy
deleted file mode 100644
index 282e36b..0000000
--- a/subprojects/core-impl/src/test/groovy/org/gradle/api/internal/externalresource/CachedExternalResourceAdapterTest.groovy
+++ /dev/null
@@ -1,83 +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.api.internal.externalresource
-
-import org.gradle.api.internal.externalresource.cached.CachedExternalResource
-import org.gradle.api.internal.externalresource.cached.CachedExternalResourceAdapter
-import org.gradle.api.internal.externalresource.metadata.DefaultExternalResourceMetaData
-import org.gradle.api.internal.externalresource.transfer.ExternalResourceAccessor
-import org.gradle.test.fixtures.file.TestNameTestDirectoryProvider
-import org.gradle.util.hash.HashUtil
-import org.gradle.util.hash.HashValue
-import org.junit.Rule
-import spock.lang.Specification
-
-public class CachedExternalResourceAdapterTest extends Specification {
-    @Rule final TestNameTestDirectoryProvider tmpDir = new TestNameTestDirectoryProvider()
-
-    ExternalResourceAccessor accessor = Mock()
-    CachedExternalResource cachedExternalResource = Mock()
-    CachedExternalResourceAdapter cachedResource
-    def origin = tmpDir.file('origin')
-    def destination = tmpDir.file('destination')
-    def download = tmpDir.file('download')
-
-    def setup() {
-        cachedExternalResource.cachedFile >> origin
-        cachedExternalResource.sha1 >> { HashUtil.createHash(origin, "SHA1") }
-        cachedResource = new CachedExternalResourceAdapter("resource-source", cachedExternalResource, accessor)
-    }
-
-    def "delegates to cached artifact"() {
-        given:
-        cachedExternalResource.contentLength >> 22
-        cachedExternalResource.externalResourceMetaData >> new DefaultExternalResourceMetaData("url")
-        cachedExternalResource.externalLastModifiedAsTimestamp >> 33
-
-        expect:
-        cachedResource.contentLength == 22
-        cachedResource.lastModified == 33
-    }
-
-    def "will copy cache file to destination"() {
-        given:
-        origin << "some content"
-
-        when:
-        cachedResource.writeTo(destination)
-
-        then:
-        destination.assertIsCopyOf(origin)
-    }
-
-    def "will copy download resource if destination does not match original sha1 after copy"() {
-        given:
-        origin << "some content"
-        download << "some other content"
-        ExternalResource resource = Mock()
-
-        when:
-        cachedResource.writeTo(destination)
-
-        then:
-        cachedExternalResource.cachedFile >> origin
-        cachedExternalResource.sha1 >> new HashValue("1234")
-
-        and:
-        accessor.getResource("resource-source") >> resource
-        resource.writeTo(destination)
-    }
-}
diff --git a/subprojects/core-impl/src/test/groovy/org/gradle/api/internal/externalresource/cached/CachedExternalResourceAdapterTest.groovy b/subprojects/core-impl/src/test/groovy/org/gradle/api/internal/externalresource/cached/CachedExternalResourceAdapterTest.groovy
new file mode 100644
index 0000000..53f0ee9
--- /dev/null
+++ b/subprojects/core-impl/src/test/groovy/org/gradle/api/internal/externalresource/cached/CachedExternalResourceAdapterTest.groovy
@@ -0,0 +1,82 @@
+/*
+ * Copyright 2011 the original author or authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.gradle.api.internal.externalresource.cached
+
+import org.gradle.api.internal.externalresource.ExternalResource
+import org.gradle.api.internal.externalresource.metadata.DefaultExternalResourceMetaData
+import org.gradle.api.internal.externalresource.transfer.ExternalResourceAccessor
+import org.gradle.test.fixtures.file.TestNameTestDirectoryProvider
+import org.gradle.util.hash.HashUtil
+import org.gradle.util.hash.HashValue
+import org.junit.Rule
+import spock.lang.Specification
+
+public class CachedExternalResourceAdapterTest extends Specification {
+    @Rule final TestNameTestDirectoryProvider tmpDir = new TestNameTestDirectoryProvider()
+
+    ExternalResourceAccessor accessor = Mock()
+    CachedExternalResource cachedExternalResource = Mock()
+    CachedExternalResourceAdapter cachedResource
+    def origin = tmpDir.file('origin')
+    def destination = tmpDir.file('destination')
+    def download = tmpDir.file('download')
+
+    def setup() {
+        cachedExternalResource.cachedFile >> origin
+        cachedExternalResource.sha1 >> { HashUtil.createHash(origin, "SHA1") }
+        cachedResource = new CachedExternalResourceAdapter("resource-source", cachedExternalResource, accessor)
+    }
+
+    def "delegates to cached artifact"() {
+        given:
+        cachedExternalResource.contentLength >> 22
+        cachedExternalResource.externalResourceMetaData >> new DefaultExternalResourceMetaData("url")
+        cachedExternalResource.externalLastModifiedAsTimestamp >> 33
+
+        expect:
+        cachedResource.contentLength == 22
+        cachedResource.lastModified == 33
+    }
+
+    def "will copy cache file to destination"() {
+        given:
+        origin << "some content"
+
+        when:
+        cachedResource.writeTo(destination)
+
+        then:
+        destination.assertIsCopyOf(origin)
+    }
+
+    def "will copy download resource if destination does not match original sha1 after copy"() {
+        given:
+        origin << "some content"
+        download << "some other content"
+        ExternalResource resource = Mock()
+
+        when:
+        cachedResource.writeTo(destination)
+
+        then:
+        cachedExternalResource.cachedFile >> origin
+        cachedExternalResource.sha1 >> new HashValue("1234")
+
+        and:
+        accessor.getResource("resource-source") >> resource
+        resource.writeTo(destination)
+    }
+}
diff --git a/subprojects/core-impl/src/test/groovy/org/gradle/api/internal/externalresource/cached/DefaultArtifactResolutionCacheTest.groovy b/subprojects/core-impl/src/test/groovy/org/gradle/api/internal/externalresource/cached/DefaultArtifactResolutionCacheTest.groovy
new file mode 100644
index 0000000..87666aa
--- /dev/null
+++ b/subprojects/core-impl/src/test/groovy/org/gradle/api/internal/externalresource/cached/DefaultArtifactResolutionCacheTest.groovy
@@ -0,0 +1,79 @@
+/*
+ * Copyright 2012 the original author or authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.gradle.api.internal.externalresource.cached
+
+import org.gradle.CacheUsage
+import org.gradle.api.internal.artifacts.ivyservice.DefaultCacheLockingManager
+import org.gradle.api.internal.externalresource.metadata.DefaultExternalResourceMetaData
+import org.gradle.cache.internal.CacheFactory
+import org.gradle.cache.internal.DefaultCacheRepository
+import org.gradle.internal.TimeProvider
+import org.gradle.test.fixtures.file.TestNameTestDirectoryProvider
+import org.gradle.testfixtures.internal.InMemoryCacheFactory
+import org.junit.Rule
+import spock.lang.Specification
+import spock.lang.Unroll
+
+class DefaultArtifactResolutionCacheTest extends Specification {
+
+    @Rule TestNameTestDirectoryProvider tmp = new TestNameTestDirectoryProvider()
+
+    Date time = new Date(0)
+    TimeProvider timeProvider = new TimeProvider() {
+        long getCurrentTime() { time.getTime() }
+    }
+
+    CacheFactory cacheFactory = new InMemoryCacheFactory()
+    DefaultCacheRepository cacheRepository
+    DefaultCacheLockingManager cacheLockingManager
+    
+    DefaultCachedExternalResourceIndex<String> index
+
+    def setup() {
+        cacheRepository = new DefaultCacheRepository(tmp.createDir('user-home'), tmp.createDir('project-cache'), CacheUsage.ON, cacheFactory)
+        cacheLockingManager = new DefaultCacheLockingManager(cacheRepository)
+        index = new DefaultCachedExternalResourceIndex(tmp.createFile("index"), String, timeProvider, cacheLockingManager)
+    }
+
+    @Unroll "stores entry - lastModified = #lastModified, artifactUrl = #artifactUrl"() {
+        given:
+        def key = "key"
+        def artifactFile = tmp.createFile("artifact") << "content"
+        
+        when:
+        index.store(key, artifactFile, new DefaultExternalResourceMetaData(artifactUrl, lastModified, 100, null, null))
+        
+        then:
+        def cached = index.lookup(key)
+        
+        and:
+        cached != null
+        cached.cachedFile == artifactFile
+        cached.externalResourceMetaData != null
+        cached.externalResourceMetaData.lastModified == lastModified
+        cached.externalResourceMetaData.location == artifactUrl
+
+
+        where:
+        lastModified | artifactUrl
+        new Date()   | null
+        null         | "abc"
+        null         | null
+        new Date()   | "abc"
+    }
+
+}
diff --git a/subprojects/core-impl/src/test/groovy/org/gradle/api/internal/externalresource/ivy/ArtifactAtRepositoryCachedArtifactIndexTest.groovy b/subprojects/core-impl/src/test/groovy/org/gradle/api/internal/externalresource/ivy/ArtifactAtRepositoryCachedArtifactIndexTest.groovy
index 7af9d1b..41d3eb4 100644
--- a/subprojects/core-impl/src/test/groovy/org/gradle/api/internal/externalresource/ivy/ArtifactAtRepositoryCachedArtifactIndexTest.groovy
+++ b/subprojects/core-impl/src/test/groovy/org/gradle/api/internal/externalresource/ivy/ArtifactAtRepositoryCachedArtifactIndexTest.groovy
@@ -16,8 +16,8 @@
 
 package org.gradle.api.internal.externalresource.ivy
 
+import org.gradle.api.artifacts.ArtifactIdentifier
 import org.gradle.api.internal.artifacts.ivyservice.CacheLockingManager
-import org.gradle.api.internal.artifacts.ivyservice.ivyresolve.CachingModuleVersionRepository
 import org.gradle.api.internal.externalresource.cached.CachedArtifact
 import org.gradle.api.internal.externalresource.metadata.ExternalResourceMetaData
 import org.gradle.cache.PersistentIndexedCache
@@ -32,8 +32,6 @@ class ArtifactAtRepositoryCachedArtifactIndexTest extends Specification {
     TimeProvider timeProvider = Mock()
     ArtifactAtRepositoryKey key = Mock()
     ExternalResourceMetaData metaData = Mock()
-    CachingModuleVersionRepository moduleVersionRepository = Mock()
-
     @Rule TestNameTestDirectoryProvider folder = new TestNameTestDirectoryProvider();
 
     PersistentIndexedCache persistentIndexedCache = Mock()
@@ -67,9 +65,8 @@ class ArtifactAtRepositoryCachedArtifactIndexTest extends Specification {
 
     def "stored artifact is put into persistentIndexedCache"() {
         setup:
-        1 * moduleVersionRepository.getId() >> "RepoID"
         1 * cacheLockingManager.createCache(persistentCacheFile, _, _) >> persistentIndexedCache
-        def key = new ArtifactAtRepositoryKey(moduleVersionRepository, "artifactId");
+        def key = new ArtifactAtRepositoryKey("RepoID", Stub(ArtifactIdentifier));
         def testFile = folder.createFile("aTestFile");
         when:
         index.store(key, testFile, BigInteger.TEN)
@@ -122,10 +119,9 @@ class ArtifactAtRepositoryCachedArtifactIndexTest extends Specification {
     }
 
     def createEntryInPersistentCache() {
-        1 * moduleVersionRepository.getId() >> "RepoID"
         1 * cacheLockingManager.createCache(persistentCacheFile, _, _) >> persistentIndexedCache
         1 * cacheLockingManager.useCache("lookup from artifact resolution cache \'cacheFile\'", _) >> {descr, factory -> factory.create()}
-        def key = new ArtifactAtRepositoryKey(moduleVersionRepository, "artifactId");
+        def key = new ArtifactAtRepositoryKey("RepoID", Stub(ArtifactIdentifier));
         1 * persistentIndexedCache.get(key) >> cachedArtifact;
         key
     }
diff --git a/subprojects/core-impl/src/test/groovy/org/gradle/api/internal/externalresource/local/CompositeLocallyAvailableResourceFinderTest.groovy b/subprojects/core-impl/src/test/groovy/org/gradle/api/internal/externalresource/local/CompositeLocallyAvailableResourceFinderTest.groovy
index cb6f7c1..20fedef 100644
--- a/subprojects/core-impl/src/test/groovy/org/gradle/api/internal/externalresource/local/CompositeLocallyAvailableResourceFinderTest.groovy
+++ b/subprojects/core-impl/src/test/groovy/org/gradle/api/internal/externalresource/local/CompositeLocallyAvailableResourceFinderTest.groovy
@@ -16,6 +16,7 @@
 
 package org.gradle.api.internal.externalresource.local
 
+import org.gradle.internal.resource.local.LocallyAvailableResource
 import spock.lang.Specification
 import org.gradle.util.hash.HashUtil
 
diff --git a/subprojects/core-impl/src/test/groovy/org/gradle/api/internal/externalresource/local/DefaultLocallyAvailableResourceTest.groovy b/subprojects/core-impl/src/test/groovy/org/gradle/api/internal/externalresource/local/DefaultLocallyAvailableResourceTest.groovy
deleted file mode 100644
index 3db4adc..0000000
--- a/subprojects/core-impl/src/test/groovy/org/gradle/api/internal/externalresource/local/DefaultLocallyAvailableResourceTest.groovy
+++ /dev/null
@@ -1,66 +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.api.internal.externalresource.local
-
-import org.gradle.test.fixtures.file.TestNameTestDirectoryProvider
-import org.gradle.util.hash.HashUtil
-import org.junit.Rule
-import spock.lang.Specification
-
-public class DefaultLocallyAvailableResourceTest extends Specification {
-    @Rule final TestNameTestDirectoryProvider tmpDir = new TestNameTestDirectoryProvider()
-
-    def "uses value from origin file"() {
-        given:
-        def origin = tmpDir.file("origin")
-        origin << "some text"
-
-        when:
-        def DefaultLocallyAvailableResource resource = new DefaultLocallyAvailableResource(origin)
-
-        then:
-        resource.sha1 == HashUtil.createHash(origin, 'SHA1')
-        resource.contentLength == origin.length()
-        resource.lastModified == origin.lastModified()
-    }
-
-    def "sha1 content length and last modified do not change when file is subsequently modified"() {
-        given:
-        def origin = tmpDir.file("origin")
-        origin << "some text"
-
-
-        when:
-        def DefaultLocallyAvailableResource resource = new DefaultLocallyAvailableResource(origin)
-        def originalSha1 = resource.sha1
-        def originalContentLength = resource.contentLength
-        def originalLastModified = resource.lastModified
-
-        and:
-        origin << "some more text"
-        origin.setLastModified(11)
-
-        then:
-        resource.sha1 != HashUtil.createHash(origin, 'SHA1')
-        resource.contentLength != origin.length()
-        resource.lastModified != origin.lastModified()
-
-        and:
-        resource.sha1 == originalSha1
-        resource.contentLength == originalContentLength
-        resource.lastModified == originalLastModified
-    }
-}
diff --git a/subprojects/core-impl/src/test/groovy/org/gradle/api/internal/externalresource/transfer/DefaultCacheAwareExternalResourceAccessorTest.groovy b/subprojects/core-impl/src/test/groovy/org/gradle/api/internal/externalresource/transfer/DefaultCacheAwareExternalResourceAccessorTest.groovy
index 13312f4..ecab39a 100644
--- a/subprojects/core-impl/src/test/groovy/org/gradle/api/internal/externalresource/transfer/DefaultCacheAwareExternalResourceAccessorTest.groovy
+++ b/subprojects/core-impl/src/test/groovy/org/gradle/api/internal/externalresource/transfer/DefaultCacheAwareExternalResourceAccessorTest.groovy
@@ -22,8 +22,8 @@ import org.gradle.api.internal.externalresource.local.LocallyAvailableResourceCa
 import org.gradle.api.internal.externalresource.cached.CachedExternalResource
 import org.gradle.api.internal.externalresource.metadata.ExternalResourceMetaData
 import org.gradle.util.hash.HashValue
-import org.gradle.api.internal.externalresource.local.LocallyAvailableResource
-import org.gradle.api.internal.externalresource.LocallyAvailableExternalResource
+import org.gradle.internal.resource.local.LocallyAvailableResource
+import org.gradle.api.internal.externalresource.DefaultLocallyAvailableExternalResource
 
 class DefaultCacheAwareExternalResourceAccessorTest extends Specification {
     final accessor = Mock(ExternalResourceAccessor)
@@ -54,6 +54,6 @@ class DefaultCacheAwareExternalResourceAccessorTest extends Specification {
         1 * localCandidates.findByHashValue(sha1) >> localCandidate
 
         and:
-        foundResource instanceof LocallyAvailableExternalResource
+        foundResource instanceof DefaultLocallyAvailableExternalResource
     }
 }
diff --git a/subprojects/core-impl/src/test/groovy/org/gradle/api/internal/externalresource/transfer/ProgressLoggingExternalResourceAccessorTest.groovy b/subprojects/core-impl/src/test/groovy/org/gradle/api/internal/externalresource/transfer/ProgressLoggingExternalResourceAccessorTest.groovy
index 259dcc3..e5168cd 100644
--- a/subprojects/core-impl/src/test/groovy/org/gradle/api/internal/externalresource/transfer/ProgressLoggingExternalResourceAccessorTest.groovy
+++ b/subprojects/core-impl/src/test/groovy/org/gradle/api/internal/externalresource/transfer/ProgressLoggingExternalResourceAccessorTest.groovy
@@ -106,19 +106,6 @@ class ProgressLoggingExternalResourceAccessorTest extends Specification {
         then:
         1 * externalResource."$method"()
         where:
-        method << ['close', 'getMetaData', 'getName', 'getLastModified', 'getContentLength', 'isLocal', 'openStream']
-    }
-
-    @Unroll
-    def "ProgressLoggingExternalResource #method to delegate ExternalResource"() {
-        when:
-        accessor.getResource("location") >> externalResource
-        def plExternalResource = progressLoggerAccessor.getResource("location")
-        and:
-        plExternalResource."$method"()
-        then:
-        1 * externalResource."$method"()
-        where:
-        method << ['close', 'getMetaData', 'getName', 'getLastModified', 'getContentLength', 'isLocal', 'openStream', 'toString']
+        method << ['close', 'getMetaData', 'getName', 'getLastModified', 'getContentLength', 'isLocal']
     }
 }
\ No newline at end of file
diff --git a/subprojects/core-impl/src/test/groovy/org/gradle/api/internal/externalresource/transport/http/ApacheDirectoryListingParserTest.groovy b/subprojects/core-impl/src/test/groovy/org/gradle/api/internal/externalresource/transport/http/ApacheDirectoryListingParserTest.groovy
index d8eb05f..62ef640 100644
--- a/subprojects/core-impl/src/test/groovy/org/gradle/api/internal/externalresource/transport/http/ApacheDirectoryListingParserTest.groovy
+++ b/subprojects/core-impl/src/test/groovy/org/gradle/api/internal/externalresource/transport/http/ApacheDirectoryListingParserTest.groovy
@@ -33,7 +33,7 @@ class ApacheDirectoryListingParserTest extends Specification {
 
     def "parse returns empty List if no link can be found"() {
         expect:
-        List urls = parser.parse(baseUrl, "<html>no link here</html>".bytes, CONTENT_TYPE)
+        List urls = parser.parse(baseUrl, new ByteArrayInputStream("<html>no link here</html>".bytes), CONTENT_TYPE)
         assertNotNull(urls)
         urls.isEmpty()
     }
@@ -55,7 +55,7 @@ class ApacheDirectoryListingParserTest extends Specification {
         <a href="directory3">directory3</a>
         <a href="directory4"/>"""
         expect:
-        def uris = parser.parse(baseUrl, html.bytes, CONTENT_TYPE)
+        def uris = parser.parse(baseUrl, new ByteArrayInputStream(html.bytes), CONTENT_TYPE)
         assertNotNull(uris)
         uris.collect {it.toString()} == ["http://testrepo/directory1", "http://testrepo/directory2", "http://testrepo/directory3", "http://testrepo/directory4"]
     }
@@ -65,7 +65,7 @@ class ApacheDirectoryListingParserTest extends Specification {
         <a href="directory1">directory1</a>
         <a href="directory2">directory2</a>"""
         when:
-        parser.parse(baseUrl, html.bytes, contentType)
+        parser.parse(baseUrl, new ByteArrayInputStream(html.bytes), contentType)
         then:
         thrown(ResourceException)
         where:
@@ -80,7 +80,7 @@ class ApacheDirectoryListingParserTest extends Specification {
         assert !Arrays.equals(encodedHtml, html.getBytes("utf-8"))
 
         expect:
-        def uris = parser.parse(baseUrl, encodedHtml, 'text/html;charset=ISO-8859-1')
+        def uris = parser.parse(baseUrl, new ByteArrayInputStream(encodedHtml), 'text/html;charset=ISO-8859-1')
         uris.collect {it.toString()} == ["http://testrepo/\u00c1\u00d2"]
     }
 
@@ -91,14 +91,14 @@ class ApacheDirectoryListingParserTest extends Specification {
         def encodedHtml = html.getBytes('utf-8')
 
         expect:
-        def uris = parser.parse(baseUrl, encodedHtml, 'text/html')
+        def uris = parser.parse(baseUrl, new ByteArrayInputStream(encodedHtml), 'text/html')
         uris.collect {it.toString()} == ["http://testrepo/\u0321\u0322"]
     }
 
     @Unroll
     def "parse ignores #descr"() {
         expect:
-        parser.parse(baseUrl, "<a href=\"${href}\">link</a>".toString().bytes, CONTENT_TYPE).isEmpty()
+        parser.parse(baseUrl, new ByteArrayInputStream("<a href=\"${href}\">link</a>".toString().bytes), CONTENT_TYPE).isEmpty()
         where:
         href                                                | descr
         "http://anothertestrepo/"                           | "URLs which aren't children of base URL"
@@ -115,7 +115,7 @@ class ApacheDirectoryListingParserTest extends Specification {
     def "parseLink handles #urlDescr"() {
         def listingParser = new ApacheDirectoryListingParser()
         expect:
-        def foundURIs = listingParser.parse(URI.create(baseUri), "<a href=\"${href}\">link</a>".toString().bytes, CONTENT_TYPE)
+        def foundURIs = listingParser.parse(URI.create(baseUri), new ByteArrayInputStream("<a href=\"${href}\">link</a>".toString().bytes), CONTENT_TYPE)
         !foundURIs.isEmpty()
         foundURIs.collect {it.toString()} == ["${baseUri}/directory1"]
         where:
@@ -137,7 +137,7 @@ class ApacheDirectoryListingParserTest extends Specification {
         setup:
         def byte[] content = resources.getResource("${repoType}_dirlisting.html").bytes
         expect:
-        List<URI> urls = new ApacheDirectoryListingParser().parse(new URI(artifactRootURI), content, CONTENT_TYPE)
+        List<URI> urls = new ApacheDirectoryListingParser().parse(new URI(artifactRootURI), new ByteArrayInputStream(content), CONTENT_TYPE)
         urls.collect {it.toString()} as Set == ["${artifactRootURI}3.7/",
                 "${artifactRootURI}3.8/",
                 "${artifactRootURI}3.8.1/",
diff --git a/subprojects/core-impl/src/test/groovy/org/gradle/api/internal/externalresource/transport/http/HttpResourceListerTest.groovy b/subprojects/core-impl/src/test/groovy/org/gradle/api/internal/externalresource/transport/http/HttpResourceListerTest.groovy
index 147bdd2..e68c664 100644
--- a/subprojects/core-impl/src/test/groovy/org/gradle/api/internal/externalresource/transport/http/HttpResourceListerTest.groovy
+++ b/subprojects/core-impl/src/test/groovy/org/gradle/api/internal/externalresource/transport/http/HttpResourceListerTest.groovy
@@ -16,6 +16,7 @@
 
 package org.gradle.api.internal.externalresource.transport.http
 
+import org.gradle.api.Transformer
 import spock.lang.Specification
 
 class HttpResourceListerTest extends Specification {
@@ -30,7 +31,7 @@ class HttpResourceListerTest extends Specification {
         when:
         lister.list("http://testrepo/")
         then:
-        1 * externalResource.writeTo(_) >> {OutputStream outputStream -> outputStream.write("<a href='child'/>".bytes)}
+        1 * externalResource.withContent(_) >> {Transformer action -> return action.transform(new ByteArrayInputStream("<a href='child'/>".bytes))}
         1 * externalResource.getContentType() >> "text/html"
         1 * externalResource.close()
     }
diff --git a/subprojects/core-impl/src/test/groovy/org/gradle/api/internal/notations/DependencyMapNotationParserTest.groovy b/subprojects/core-impl/src/test/groovy/org/gradle/api/internal/notations/DependencyMapNotationParserTest.groovy
index c5b2231..8d71ab8 100644
--- a/subprojects/core-impl/src/test/groovy/org/gradle/api/internal/notations/DependencyMapNotationParserTest.groovy
+++ b/subprojects/core-impl/src/test/groovy/org/gradle/api/internal/notations/DependencyMapNotationParserTest.groovy
@@ -14,17 +14,13 @@
  * limitations under the License.
  */
 
-package org.gradle.api.internal.notations;
-
+package org.gradle.api.internal.notations
 
 import org.gradle.api.artifacts.DependencyArtifact
-import org.gradle.internal.reflect.DirectInstantiator
 import org.gradle.api.internal.artifacts.dependencies.DefaultExternalModuleDependency
+import org.gradle.internal.reflect.DirectInstantiator
 import spock.lang.Specification
 
-/**
- * by Szczepan Faber, created at: 11/10/11
- */
 public class DependencyMapNotationParserTest extends Specification {
 
     def parser = new DependencyMapNotationParser<DefaultExternalModuleDependency>(new DirectInstantiator(), DefaultExternalModuleDependency.class);
diff --git a/subprojects/core-impl/src/test/groovy/org/gradle/api/internal/notations/DependencyNotationParserTest.groovy b/subprojects/core-impl/src/test/groovy/org/gradle/api/internal/notations/DependencyNotationParserTest.groovy
index 2fdda25..7a01230 100644
--- a/subprojects/core-impl/src/test/groovy/org/gradle/api/internal/notations/DependencyNotationParserTest.groovy
+++ b/subprojects/core-impl/src/test/groovy/org/gradle/api/internal/notations/DependencyNotationParserTest.groovy
@@ -21,9 +21,6 @@ import org.gradle.api.GradleException
 import org.gradle.api.internal.notations.api.NotationParser
 import spock.lang.Specification
 
-/**
- * by Szczepan Faber, created at: 11/9/11
- */
 public class DependencyNotationParserTest extends Specification {
 
     def notationParser = Mock(NotationParser)
diff --git a/subprojects/core-impl/src/test/groovy/org/gradle/api/internal/notations/DependencyStringNotationParserTest.groovy b/subprojects/core-impl/src/test/groovy/org/gradle/api/internal/notations/DependencyStringNotationParserTest.groovy
index 387701c..a6c99b5 100644
--- a/subprojects/core-impl/src/test/groovy/org/gradle/api/internal/notations/DependencyStringNotationParserTest.groovy
+++ b/subprojects/core-impl/src/test/groovy/org/gradle/api/internal/notations/DependencyStringNotationParserTest.groovy
@@ -14,19 +14,15 @@
  * limitations under the License.
  */
 
-package org.gradle.api.internal.notations;
-
+package org.gradle.api.internal.notations
 
 import org.gradle.api.artifacts.DependencyArtifact
-import org.gradle.internal.reflect.DirectInstantiator
 import org.gradle.api.internal.artifacts.dependencies.DefaultClientModule
 import org.gradle.api.internal.artifacts.dependencies.DefaultExternalModuleDependency
-import org.gradle.util.HelperUtil
+import org.gradle.internal.reflect.DirectInstantiator
+import org.gradle.util.TestUtil
 import spock.lang.Specification
 
-/**
- * by Szczepan Faber, created at: 11/10/11
- */
 public class DependencyStringNotationParserTest extends Specification {
 
     def parser = new DependencyStringNotationParser(new DirectInstantiator(), DefaultExternalModuleDependency.class);
@@ -85,7 +81,7 @@ public class DependencyStringNotationParserTest extends Specification {
 
     def "with 3-element GString"() {
         when:
-        def gstring = HelperUtil.createScript("descriptor = 'org.gradle:gradle-core:1.0'; \"\$descriptor\"").run()
+        def gstring = TestUtil.createScript("descriptor = 'org.gradle:gradle-core:1.0'; \"\$descriptor\"").run()
         def d = parser.parseNotation(gstring);
 
         then:
diff --git a/subprojects/core-impl/src/test/groovy/org/gradle/api/internal/notations/ProjectDependencyFactoryTest.groovy b/subprojects/core-impl/src/test/groovy/org/gradle/api/internal/notations/ProjectDependencyFactoryTest.groovy
index 7b877c6..32a6d94 100644
--- a/subprojects/core-impl/src/test/groovy/org/gradle/api/internal/notations/ProjectDependencyFactoryTest.groovy
+++ b/subprojects/core-impl/src/test/groovy/org/gradle/api/internal/notations/ProjectDependencyFactoryTest.groovy
@@ -25,9 +25,6 @@ import org.gradle.internal.reflect.DirectInstantiator
 import org.gradle.util.GUtil
 import spock.lang.Specification
 
-/**
- * @author Hans Dockter
- */
 public class ProjectDependencyFactoryTest extends Specification {
 
     def projectDummy = Mock(ProjectInternal)
diff --git a/subprojects/core-impl/src/test/resources/org/gradle/api/internal/artifacts/ivyservice/ivyresolve/parser/test-bad-confs.xml b/subprojects/core-impl/src/test/resources/org/gradle/api/internal/artifacts/ivyservice/ivyresolve/parser/test-bad-confs.xml
deleted file mode 100644
index 7287882..0000000
--- a/subprojects/core-impl/src/test/resources/org/gradle/api/internal/artifacts/ivyservice/ivyresolve/parser/test-bad-confs.xml
+++ /dev/null
@@ -1,27 +0,0 @@
-<!--
-   Licensed to the Apache Software Foundation (ASF) under one
-   or more contributor license agreements.  See the NOTICE file
-   distributed with this work for additional information
-   regarding copyright ownership.  The ASF licenses this file
-   to you under the Apache License, Version 2.0 (the
-   "License"); you may not use this file except in compliance
-   with the License.  You may obtain a copy of the License at
-
-     http://www.apache.org/licenses/LICENSE-2.0
-
-   Unless required by applicable law or agreed to in writing,
-   software distributed under the License is distributed on an
-   "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
-   KIND, either express or implied.  See the License for the
-   specific language governing permissions and limitations
-   under the License.    
--->
-<ivy-module version="1.0">
-	<info  organisation="myorg"
-	       module="mymodule"
-	       status="integration"
-	/>
-	<configurations>
-		<conf name="A" extends="invalidConf"/>
-	</configurations>
-</ivy-module>
diff --git a/subprojects/core-impl/src/test/resources/org/gradle/api/internal/artifacts/ivyservice/ivyresolve/parser/test-cyclic-confs1.xml b/subprojects/core-impl/src/test/resources/org/gradle/api/internal/artifacts/ivyservice/ivyresolve/parser/test-cyclic-confs1.xml
deleted file mode 100644
index 3890369..0000000
--- a/subprojects/core-impl/src/test/resources/org/gradle/api/internal/artifacts/ivyservice/ivyresolve/parser/test-cyclic-confs1.xml
+++ /dev/null
@@ -1,28 +0,0 @@
-<!--
-   Licensed to the Apache Software Foundation (ASF) under one
-   or more contributor license agreements.  See the NOTICE file
-   distributed with this work for additional information
-   regarding copyright ownership.  The ASF licenses this file
-   to you under the Apache License, Version 2.0 (the
-   "License"); you may not use this file except in compliance
-   with the License.  You may obtain a copy of the License at
-
-     http://www.apache.org/licenses/LICENSE-2.0
-
-   Unless required by applicable law or agreed to in writing,
-   software distributed under the License is distributed on an
-   "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
-   KIND, either express or implied.  See the License for the
-   specific language governing permissions and limitations
-   under the License.    
--->
-<ivy-module version="1.0">
-	<info  organisation="myorg"
-	       module="mymodule"
-	       status="integration"
-	/>
-	<configurations>
-		<conf name="A" extends="B"/>
-		<conf name="B" extends="A"/>
-	</configurations>
-</ivy-module>
diff --git a/subprojects/core-impl/src/test/resources/org/gradle/api/internal/artifacts/ivyservice/ivyresolve/parser/test-empty-dependencies.xml b/subprojects/core-impl/src/test/resources/org/gradle/api/internal/artifacts/ivyservice/ivyresolve/parser/test-empty-dependencies.xml
deleted file mode 100644
index 3240dc4..0000000
--- a/subprojects/core-impl/src/test/resources/org/gradle/api/internal/artifacts/ivyservice/ivyresolve/parser/test-empty-dependencies.xml
+++ /dev/null
@@ -1,28 +0,0 @@
-<!--
-   Licensed to the Apache Software Foundation (ASF) under one
-   or more contributor license agreements.  See the NOTICE file
-   distributed with this work for additional information
-   regarding copyright ownership.  The ASF licenses this file
-   to you under the Apache License, Version 2.0 (the
-   "License"); you may not use this file except in compliance
-   with the License.  You may obtain a copy of the License at
-
-     http://www.apache.org/licenses/LICENSE-2.0
-
-   Unless required by applicable law or agreed to in writing,
-   software distributed under the License is distributed on an
-   "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
-   KIND, either express or implied.  See the License for the
-   specific language governing permissions and limitations
-   under the License.    
--->
-<ivy-module version="1.0">
-	<info organisation="myorg"
-	       module="mymodule"
-	       revision="myrev"
-	       status="integration"
-	       publication="20041101110000"
-	/>
-	<dependencies>
-	</dependencies>
-</ivy-module>
diff --git a/subprojects/core-impl/src/test/resources/org/gradle/api/internal/artifacts/ivyservice/ivyresolve/parser/test-full.xml b/subprojects/core-impl/src/test/resources/org/gradle/api/internal/artifacts/ivyservice/ivyresolve/parser/test-full.xml
index dfb4a55..dbc6812 100644
--- a/subprojects/core-impl/src/test/resources/org/gradle/api/internal/artifacts/ivyservice/ivyresolve/parser/test-full.xml
+++ b/subprojects/core-impl/src/test/resources/org/gradle/api/internal/artifacts/ivyservice/ivyresolve/parser/test-full.xml
@@ -26,12 +26,10 @@
 	       e:attr1="value1">
 	       
 		<license name="MyLicense" url="http://www.my.org/mymodule/mylicense.html"/>
-		<repository name="ivyrep" url="http://www.jayasoft.fr/org/ivyrep/" pattern="[organisation]/[module]/ivy-[revision].xml" ivys="true" artifacts="false"/>
-		
 		<ivyauthor name="jayasoft" url="http://www.jayasoft.org/"/>
 		<ivyauthor name="myorg" url="http://www.myorg.org/"/>
+		<repository name="ivyrep" url="http://www.jayasoft.fr/org/ivyrep/" pattern="[organisation]/[module]/ivy-[revision].xml" ivys="true" artifacts="false"/>
 
-		
 		<description homepage="http://www.my.org/mymodule/">			
 	This module is <b>great</b> !<br/>
 	You can use it especially with myconf1 and myconf2, and myconf4 is not too bad too.
diff --git a/subprojects/core-impl/src/testFixtures/groovy/org/gradle/api/internal/artifacts/result/ResolutionResultDataBuilder.groovy b/subprojects/core-impl/src/testFixtures/groovy/org/gradle/api/internal/artifacts/result/ResolutionResultDataBuilder.groovy
index da4afe7..d8138d4 100644
--- a/subprojects/core-impl/src/testFixtures/groovy/org/gradle/api/internal/artifacts/result/ResolutionResultDataBuilder.groovy
+++ b/subprojects/core-impl/src/testFixtures/groovy/org/gradle/api/internal/artifacts/result/ResolutionResultDataBuilder.groovy
@@ -17,15 +17,12 @@
 package org.gradle.api.internal.artifacts.result
 
 import org.gradle.api.artifacts.result.ModuleVersionSelectionReason
+import org.gradle.api.internal.artifacts.ivyservice.ModuleVersionResolveException
 import org.gradle.api.internal.artifacts.ivyservice.resolveengine.result.VersionSelectionReasons
 
 import static org.gradle.api.internal.artifacts.DefaultModuleVersionIdentifier.newId
 import static org.gradle.api.internal.artifacts.DefaultModuleVersionSelector.newSelector
-import org.gradle.api.internal.artifacts.ivyservice.ModuleVersionResolveException
 
-/**
- * by Szczepan Faber, created at: 10/2/12
- */
 class ResolutionResultDataBuilder {
 
     static DefaultResolvedDependencyResult newDependency(String group='a', String module='a', String version='1', String selectedVersion='1') {
diff --git a/subprojects/core/core.gradle b/subprojects/core/core.gradle
index fd5a202..bcfa2b5 100755
--- a/subprojects/core/core.gradle
+++ b/subprojects/core/core.gradle
@@ -21,13 +21,14 @@ configurations {
 }
 
 dependencies {
-    groovy libraries.groovy
+    compile libraries.groovy
 
     publishCompile libraries.slf4j_api
     publishCompile project(":baseServices")
     publishCompile project(":messaging")
 
     compile project(":baseServicesGroovy")
+    compile project(":resources")
     compile libraries.asm
     compile libraries.ant
     compile libraries.commons_collections
@@ -45,6 +46,8 @@ dependencies {
     compile project(":cli")
     compile project(":native")
 
+    runtime project(":docs")
+
     runtime libraries.log4j_to_slf4j
     runtime libraries.jcl_to_slf4j
 
@@ -64,6 +67,7 @@ dependencies {
 }
 
 useTestFixtures()
+useTestFixtures(project: ":messaging")
 
 [compileGroovy, compileTestGroovy]*.groovyOptions*.fork(memoryInitialSize: '128M', memoryMaximumSize: '1G')
 
diff --git a/subprojects/core/src/integTest/groovy/org/gradle/DeprecationHandlingIntegrationTest.groovy b/subprojects/core/src/integTest/groovy/org/gradle/DeprecationHandlingIntegrationTest.groovy
new file mode 100644
index 0000000..031ee8d
--- /dev/null
+++ b/subprojects/core/src/integTest/groovy/org/gradle/DeprecationHandlingIntegrationTest.groovy
@@ -0,0 +1,125 @@
+/*
+ * Copyright 2013 the original author or authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.gradle
+
+import org.gradle.integtests.fixtures.AbstractIntegrationSpec
+
+class DeprecationHandlingIntegrationTest extends AbstractIntegrationSpec {
+    def "reports first usage of deprecated feature from a build script"() {
+        buildFile << """
+
+someFeature()
+someFeature()
+task broken(type: DeprecatedTask) {
+    otherFeature()
+}
+
+repositories {
+    mavenRepo url: 'build/repo'
+}
+
+def someFeature() {
+    DeprecationLogger.nagUserOfDiscontinuedMethod("someFeature()")
+}
+
+class DeprecatedTask extends DefaultTask {
+    def otherFeature() {
+        DeprecationLogger.nagUserOfDiscontinuedMethod("otherFeature()")
+    }
+}
+"""
+
+        when:
+        executer.withDeprecationChecksDisabled()
+        run()
+
+        then:
+        output.contains("Build file '$buildFile': line 3")
+        output.count("The someFeature() method has been deprecated") == 1
+        output.contains("Build file '$buildFile': line 6")
+        output.count("The otherFeature() method has been deprecated") == 1
+        output.contains("Build file '$buildFile': line 10")
+        output.count("The RepositoryHandler.mavenRepo() method has been deprecated") == 1
+
+        // Run again to ensure logging is reset
+        when:
+        executer.withDeprecationChecksDisabled()
+        run()
+
+        then:
+        output.contains("Build file '$buildFile': line 3")
+        output.count("The someFeature() method has been deprecated") == 1
+        output.contains("Build file '$buildFile': line 6")
+        output.count("The otherFeature() method has been deprecated") == 1
+        output.contains("Build file '$buildFile': line 10")
+        output.count("The RepositoryHandler.mavenRepo() method has been deprecated") == 1
+
+        // Not shown at quiet level
+        when:
+        executer.withArgument("--quiet")
+        run()
+
+        then:
+        output.count("The someFeature() method has been deprecated") == 0
+        output.count("The otherFeature() method has been deprecated") == 0
+        output.count("The RepositoryHandler.mavenRepo() method has been deprecated") == 0
+        errorOutput == ""
+    }
+
+    def "reports usage of deprecated feature from an init script"() {
+        def initScript = file("init.gradle") << """
+allprojects {
+    someFeature()
+}
+
+def someFeature() {
+    DeprecationLogger.nagUserOfDiscontinuedMethod("someFeature()")
+}
+
+"""
+
+        when:
+        executer.withDeprecationChecksDisabled().usingInitScript(initScript)
+        run()
+
+        then:
+        output.contains("Initialization script '$initScript': line 3")
+        output.count("The someFeature() method has been deprecated") == 1
+        errorOutput == ""
+    }
+
+    def "reports usage of deprecated feature from an applied script"() {
+        def script = file("project.gradle") << """
+
+def someFeature() {
+    DeprecationLogger.nagUserOfDiscontinuedMethod("someFeature()")
+}
+
+someFeature()
+"""
+        buildFile << "allprojects { apply from: 'project.gradle' }"
+
+        when:
+        executer.withDeprecationChecksDisabled()
+        run()
+
+        then:
+        output.contains("Script '$script': line 7")
+        output.count("The someFeature() method has been deprecated") == 1
+        errorOutput == ""
+    }
+}
diff --git a/subprojects/core/src/integTest/groovy/org/gradle/api/ApplyPluginIntegSpec.groovy b/subprojects/core/src/integTest/groovy/org/gradle/api/ApplyPluginIntegSpec.groovy
new file mode 100644
index 0000000..0762af7
--- /dev/null
+++ b/subprojects/core/src/integTest/groovy/org/gradle/api/ApplyPluginIntegSpec.groovy
@@ -0,0 +1,84 @@
+/*
+ * Copyright 2013 the original author or authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF 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.integtests.fixtures.AbstractIntegrationSpec
+import org.gradle.integtests.fixtures.executer.UnexpectedBuildFailure
+import org.gradle.testfixtures.ProjectBuilder
+import spock.lang.FailsWith
+import spock.lang.Issue
+
+// TODO: This needs a better home - Possibly in the test kit package in the future
+class ApplyPluginIntegSpec extends AbstractIntegrationSpec {
+
+    @Issue("GRADLE-2358")
+    @FailsWith(UnexpectedBuildFailure) // Test is currently failing
+    def "can reference plugin by id in unitest"() {
+
+        given:
+        file("src/main/groovy/org/acme/TestPlugin.groovy") << """
+            package com.acme
+            import org.gradle.api.*
+
+            class TestPlugin implements Plugin<Project> {
+                void apply(Project project) {
+                    println "testplugin applied"
+                }
+            }
+        """
+
+        file("src/main/resources/META-INF/gradle-plugins/testplugin.properties") << "implementation-class=org.acme.TestPlugin"
+
+        file("src/test/groovy/org/acme/TestPluginSpec.groovy") << """
+            import spock.lang.Specification
+            import ${ProjectBuilder.name}
+            import ${Project.name}
+            import com.acme.TestPlugin
+
+            class TestPluginSpec extends Specification {
+                def "can apply plugin by id"() {
+                    when:
+                    Project project = ProjectBuilder.builder().build()
+                    project.apply(plugin: "testplugin")
+
+                    then:
+                    project.plugins.withType(TestPlugin).size() == 1
+                }
+            }
+        """
+
+        and:
+        buildFile << '''
+            apply plugin: 'groovy'
+
+            repositories {
+                mavenCentral()
+            }
+
+            dependencies {
+                compile gradleApi()
+                compile localGroovy()
+                compile "org.spockframework:spock-core:0.7-groovy-1.8", {
+                    exclude module: "groovy-all"
+                }
+            }
+        '''
+
+        expect:
+        succeeds("test")
+    }
+}
\ No newline at end of file
diff --git a/subprojects/core/src/integTest/groovy/org/gradle/api/BuildScriptErrorIntegrationTest.groovy b/subprojects/core/src/integTest/groovy/org/gradle/api/BuildScriptErrorIntegrationTest.groovy
new file mode 100755
index 0000000..f4df3c8
--- /dev/null
+++ b/subprojects/core/src/integTest/groovy/org/gradle/api/BuildScriptErrorIntegrationTest.groovy
@@ -0,0 +1,98 @@
+/*
+ * Copyright 2013 the original author or authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF 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.integtests.fixtures.AbstractIntegrationSpec
+
+import static org.hamcrest.Matchers.containsString
+
+public class BuildScriptErrorIntegrationTest extends AbstractIntegrationSpec {
+
+    def "setup"() {
+        settingsFile << "rootProject.name = 'ProjectError'"
+    }
+
+    def "produces reasonable error message when buildFile evaluation fails with Groovy Exception"() {
+        buildFile << """
+    createTakk('do-stuff')
+"""
+        when:
+        fails()
+
+        then:
+        failure.assertHasDescription("A problem occurred evaluating root project 'ProjectError'.")
+                .assertHasCause("Could not find method createTakk() for arguments [do-stuff] on root project 'ProjectError")
+                .assertHasFileName("Build file '$buildFile'")
+                .assertHasLineNumber(2)
+    }
+
+    def "produces reasonable error message when buildFile evaluation fails on script compilation"() {
+        buildFile << """
+    // a comment
+    import org.gradle.unknown.Unknown
+    new Unknown()
+"""
+
+        when:
+        fails()
+
+        then:
+        failure.assertHasDescription("Could not compile build file '${buildFile}'.")
+                .assertThatCause(containsString("build file '$buildFile': 3: unable to resolve class org.gradle.unknown.Unknown"))
+                .assertHasFileName("Build file '$buildFile'")
+                .assertHasLineNumber(3)
+    }
+
+    def "produces reasonable error message when buildFile evaluation fails with exception"() {
+        when:
+        buildFile << """
+    throw new RuntimeException("script failure")
+    task test
+"""
+        then:
+        fails('test')
+        failure.assertHasDescription("A problem occurred evaluating root project 'ProjectError'.")
+                .assertHasCause("script failure")
+                .assertHasFileName("Build file '${buildFile.path}'")
+                .assertHasLineNumber(2)
+    }
+
+    def "produces reasonable error message when nested buildFile evaluation fails"() {
+        settingsFile << """
+include 'child'
+"""
+        buildFile << """
+    evaluationDependsOn 'child'
+    task t
+"""
+        final childBuildFile = file("child/build.gradle")
+        childBuildFile << """
+    def broken = { ->
+        throw new RuntimeException('failure')
+    }
+    broken()
+"""
+
+        when:
+        fails()
+
+        then:
+        failure.assertHasDescription("A problem occurred evaluating project ':child'.")
+                .assertHasCause("failure")
+                .assertHasFileName("Build file '$childBuildFile'")
+                .assertHasLineNumber(3)
+    }
+}
diff --git a/subprojects/core/src/integTest/groovy/org/gradle/api/ConfigurationOnDemandIntegrationTest.groovy b/subprojects/core/src/integTest/groovy/org/gradle/api/ConfigurationOnDemandIntegrationTest.groovy
index 6d50251..a14305d 100644
--- a/subprojects/core/src/integTest/groovy/org/gradle/api/ConfigurationOnDemandIntegrationTest.groovy
+++ b/subprojects/core/src/integTest/groovy/org/gradle/api/ConfigurationOnDemandIntegrationTest.groovy
@@ -17,12 +17,11 @@
 package org.gradle.api
 
 import org.gradle.integtests.fixtures.AbstractIntegrationSpec
+import org.gradle.integtests.fixtures.executer.GradleContextualExecuter
 import org.gradle.integtests.fixtures.executer.ProjectLifecycleFixture
 import org.junit.Rule
+import spock.lang.IgnoreIf
 
-/**
- * by Szczepan Faber, created at: 11/21/12
- */
 class ConfigurationOnDemandIntegrationTest extends AbstractIntegrationSpec {
 
     @Rule ProjectLifecycleFixture fixture = new ProjectLifecycleFixture(executer, temporaryFolder)
@@ -31,13 +30,41 @@ class ConfigurationOnDemandIntegrationTest extends AbstractIntegrationSpec {
         file("gradle.properties") << "org.gradle.configureondemand=true"
     }
 
-    def "works with single-module project"() {
+    @IgnoreIf({ GradleContextualExecuter.isParallel() }) //parallel mode hides incubating message
+    def "presents incubating message"() {
+        file("gradle.properties") << "org.gradle.configureondemand=false"
         buildFile << "task foo"
+
         when:
-        run("foo")
+        run("foo", "--configure-on-demand")
+
+        then:
+        fixture.assertProjectsConfigured(":")
+        output.count("Configuration on demand is an incubating feature") == 1
+    }
+
+    @IgnoreIf({ GradleContextualExecuter.isParallel() }) //parallel mode hides incubating message
+    def "presents incubating message with parallel mode"() {
+        file("gradle.properties") << "org.gradle.configureondemand=false"
+        buildFile << "task foo"
+
+        when:
+        run("foo", "--configure-on-demand", "--parallel")
+
+        then:
+        fixture.assertProjectsConfigured(":")
+        output.count("Parallel execution with configuration on demand is an incubating feature") == 1
+    }
+
+    def "can be enabled from command line for a single module build"() {
+        file("gradle.properties") << "org.gradle.configureondemand=false"
+        buildFile << "task foo"
+
+        when:
+        run("foo", "--configure-on-demand")
+
         then:
         fixture.assertProjectsConfigured(":")
-        assert output.contains("Configuration on demand is an incubating feature")
     }
 
     def "evaluates only project referenced in the task list"() {
@@ -49,15 +76,14 @@ class ConfigurationOnDemandIntegrationTest extends AbstractIntegrationSpec {
 
         then:
         fixture.assertProjectsConfigured(":", ":util:impl")
-        assert output.contains("Configuration on demand is an incubating feature")
     }
 
-    def "does not show configuration on demand message in a regular mode"() {
+    def "does not show configuration on demand incubating message in a regular mode"() {
         file("gradle.properties").text = "org.gradle.configureondemand=false"
         when:
         run()
         then:
-        assert !output.contains("Configuration on demand is an incubating feature")
+        !output.contains("Configuration on demand is incubating")
     }
 
     def "follows java project dependencies"() {
@@ -106,6 +132,26 @@ class ConfigurationOnDemandIntegrationTest extends AbstractIntegrationSpec {
         fixture.assertProjectsConfigured(":", ":util", ":impl", ":api")
     }
 
+    def "can have cycles in project dependencies"() {
+        settingsFile << "include 'api', 'impl', 'util'"
+        buildFile << """
+allprojects { apply plugin: 'java' }
+project(':impl') {
+    dependencies { compile project(path: ':api', configuration: 'archives') }
+}
+project(':api') {
+    dependencies { runtime project(':impl') }
+    task run(dependsOn: configurations.runtime)
+}
+"""
+
+        when:
+        run(":api:run")
+
+        then:
+        fixture.assertProjectsConfigured(":", ":api", ':impl')
+    }
+
     def "follows project dependencies when ran in subproject"() {
         settingsFile << "include 'api', 'impl', 'util'"
 
@@ -220,12 +266,9 @@ class ConfigurationOnDemandIntegrationTest extends AbstractIntegrationSpec {
         run("impl:build", "--no-rebuild") // impl -> api
 
         then:
-        //api tasks are not executed
+        //api tasks are not executed and api is not configured
         !result.executedTasks.find { it.startsWith ":api" }
-        //but the api project is still configured
-        //ideally, the ':api' project is not configured in the configure on demand mode
-        //but this is complicated to implement so lets leave it for now
-        fixture.assertProjectsConfigured(":", ":impl", ":api")
+        fixture.assertProjectsConfigured(":", ":impl")
     }
 
     def "respects external task dependencies"() {
@@ -258,8 +301,30 @@ class ConfigurationOnDemandIntegrationTest extends AbstractIntegrationSpec {
         buildFile << "task foo(type: FooTask)"
 
         when:
-        run("foo")
+        run("foo", "-s")
         then:
         output.contains "Horray!!!"
     }
+
+    def "may configure project at execution time"() {
+        settingsFile << "include 'a', 'b', 'c'"
+        file('a/build.gradle') << """
+            configurations { conf }
+            dependencies { conf project(path: ":b", configuration: "conf") }
+            task resolveConf << {
+              //resolves at execution time, forcing 'b' to get configured
+              configurations.conf.files
+            }
+        """
+
+        file('b/build.gradle') << """
+            configurations { conf }
+        """
+
+        when:
+        run(":a:resolveConf", "-i")
+
+        then:
+        fixture.assertProjectsConfigured(":", ":a", ":b")
+    }
 }
\ No newline at end of file
diff --git a/subprojects/core/src/integTest/groovy/org/gradle/api/CrossProcessFileLockIntegrationTest.groovy b/subprojects/core/src/integTest/groovy/org/gradle/api/CrossProcessFileLockIntegrationTest.groovy
new file mode 100644
index 0000000..769cada
--- /dev/null
+++ b/subprojects/core/src/integTest/groovy/org/gradle/api/CrossProcessFileLockIntegrationTest.groovy
@@ -0,0 +1,71 @@
+/*
+ * Copyright 2013 the original author or authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF 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.integtests.fixtures.AbstractIntegrationSpec
+
+import static org.gradle.test.fixtures.ConcurrentTestUtil.poll
+
+public class CrossProcessFileLockIntegrationTest extends AbstractIntegrationSpec {
+
+    def "the task history lock can be acquired when the initial owner is busy executing tasks"() {
+        settingsFile << "include 'a', 'b'"
+
+        file("a/src/main/java/A.java") << "public class A {}"
+        file("b/src/main/java/B.java") << "public class B {}"
+
+        when:
+        buildFile << """
+            def waitForStop() {
+              def sanityWaitUntil = System.currentTimeMillis() + 120000
+              println 'waiting for file...'
+              while(!file('stop.txt').exists()) {
+                Thread.sleep(300)
+                assert System.currentTimeMillis() < sanityWaitUntil : "Timeout waiting for file"
+              }
+              println 'no more waiting!'
+            }
+            def stopNow() {
+              assert file('stop.txt').createNewFile()
+            }
+            subprojects {
+                apply plugin: 'java'
+            }
+            project(":a") {
+                compileJava.doFirst { waitForStop() }
+            }
+            project(":b") {
+                compileJava.doFirst { stopNow() }
+            }
+        """
+
+        then:
+        def handle1 = executer.withArguments(':a:build', '-i').start()
+        poll(120) {
+            assert handle1.standardOutput.contains('waiting for file...')
+        }
+        //first build is waiting for file, so the lock should be releasable now (for example: the task history lock)
+
+        and:
+        def handle2 = executer.withArguments('b:build', '-is').start()
+        handle2.waitForFinish()
+        handle1.waitForFinish()
+
+        and:
+        file("a/build/libs/a.jar").assertExists()
+        file("b/build/libs/b.jar").assertExists()
+    }
+}
diff --git a/subprojects/core/src/integTest/groovy/org/gradle/api/DeferredConfigurableExtensionIntegrationTest.groovy b/subprojects/core/src/integTest/groovy/org/gradle/api/DeferredConfigurableExtensionIntegrationTest.groovy
new file mode 100755
index 0000000..f70c11a
--- /dev/null
+++ b/subprojects/core/src/integTest/groovy/org/gradle/api/DeferredConfigurableExtensionIntegrationTest.groovy
@@ -0,0 +1,174 @@
+/*
+ * Copyright 2013 the original author or authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF 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.integtests.fixtures.AbstractIntegrationSpec
+
+public class DeferredConfigurableExtensionIntegrationTest extends AbstractIntegrationSpec {
+
+    def "setup"() {
+        settingsFile << "rootProject.name = 'customProject'"
+
+        buildFile << """
+public class CustomPlugin implements Plugin<Project> {
+    public void apply(Project project) {
+        project.getExtensions().create("custom", CustomExtension.class)
+    }
+}
+
+public class BrokenCustomPlugin implements Plugin<Project> {
+    public void apply(Project project) {
+        project.getPlugins().apply(CustomPlugin)
+        project.getExtensions().configure(CustomExtension, {
+            throw new RuntimeException("broken configuration in plugin")
+        } as Action)
+    }
+}
+
+ at org.gradle.api.plugins.DeferredConfigurable
+public class CustomExtension {
+    private final StringBuilder builder = new StringBuilder()
+    public void append(String value) {
+        builder.append(value)
+    }
+
+    public String getString() {
+        return builder.toString()
+    }
+}
+"""
+    }
+
+    private static def buildFileLine(int number) {
+        return number + 28 // Number of lines added to build script in setup
+    }
+
+    def "configure actions on deferred configurable extension are deferred until access"() {
+        when:
+        buildFile << '''
+apply plugin: CustomPlugin
+
+version = "1"
+custom {
+    append project.version
+}
+
+version = "2"
+custom {
+    append project.version
+}
+
+assert custom.string == "22"
+task test
+'''
+        then:
+        succeeds('test')
+    }
+
+    def "configure actions on deferred configurable extension are not applied if extension is not referenced"() {
+        when:
+        buildFile << '''
+apply plugin: CustomPlugin
+
+custom {
+    throw new RuntimeException()
+}
+
+task test
+'''
+        then:
+        succeeds('test')
+    }
+
+    def "reports on failure in deferred configurable that is referenced in the build"() {
+        when:
+        buildFile << '''
+apply plugin: CustomPlugin
+custom {
+    throw new RuntimeException("deferred configuration failure")
+}
+assert custom.string == "22"
+task test
+'''
+        then:
+        fails 'test'
+        failure.assertHasDescription("A problem occurred evaluating root project 'customProject'.")
+                .assertHasCause("deferred configuration failure")
+                .assertHasFileName("Build file '${buildFile.path}'")
+                .assertHasLineNumber(buildFileLine(3))
+    }
+
+    def "reports on failure in deferred configurable that is configured in plugin"() {
+        when:
+        buildFile << '''
+apply plugin: BrokenCustomPlugin
+print custom.string
+task test
+'''
+        then:
+        fails 'test'
+        failure.assertHasDescription("A problem occurred evaluating root project 'customProject'.")
+                .assertHasCause("broken configuration in plugin")
+                .assertHasFileName("Build file '${buildFile.path}'")
+                .assertHasLineNumber(12)
+    }
+
+    def "does not report on deferred configuration failure in case of another configuration failure"() {
+        when:
+        buildFile << '''
+apply plugin: BrokenCustomPlugin
+custom {
+    throw new RuntimeException("deferred configuration failure")
+}
+task test {
+    throw new RuntimeException("task configuration failure")
+}
+afterEvaluate {
+    project.custom
+}
+'''
+        then:
+        fails 'test'
+        failure.assertHasDescription("A problem occurred evaluating root project 'customProject'.")
+                .assertHasCause("task configuration failure")
+                .assertHasFileName("Build file '${buildFile.path}'")
+                .assertHasLineNumber(buildFileLine(6))
+    }
+
+    def "cannot configure deferred configurable extension after access"() {
+        when:
+        buildFile << '''
+apply plugin: CustomPlugin
+
+version = "1"
+custom {
+    append project.version
+}
+
+assert custom.string == "1"
+
+custom {
+    append project.version
+}
+task test
+'''
+        then:
+        fails('test')
+        failure.assertHasDescription("A problem occurred evaluating root project 'customProject'.")
+                .assertHasCause("Cannot configure the 'custom' extension after it has been accessed.")
+                .assertHasFileName("Build file '${buildFile.path}'")
+                .assertHasLineNumber(buildFileLine(10))
+    }
+}
diff --git a/subprojects/core/src/integTest/groovy/org/gradle/api/ExternalScriptErrorIntegrationTest.groovy b/subprojects/core/src/integTest/groovy/org/gradle/api/ExternalScriptErrorIntegrationTest.groovy
new file mode 100755
index 0000000..a3e0a21
--- /dev/null
+++ b/subprojects/core/src/integTest/groovy/org/gradle/api/ExternalScriptErrorIntegrationTest.groovy
@@ -0,0 +1,87 @@
+/*
+ * Copyright 2013 the original author or authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF 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.integtests.fixtures.AbstractIntegrationSpec
+
+import static org.hamcrest.Matchers.containsString
+
+class ExternalScriptErrorIntegrationTest extends AbstractIntegrationSpec {
+    def externalScript
+
+    def "setup"() {
+        externalScript = file('other.gradle')
+        settingsFile << "rootProject.name = 'project'"
+        buildFile << """
+    apply { from 'other.gradle' }
+"""
+    }
+
+    def "produces reasonable error message when external script fails with Groovy exception"() {
+        externalScript << '''
+
+doStuff()
+'''
+        when:
+        fails()
+
+        then:
+        failure.assertHasDescription('A problem occurred evaluating script.')
+                .assertHasCause('Could not find method doStuff() for arguments [] on root project')
+                .assertHasFileName("Script '${externalScript}'")
+                .assertHasLineNumber(3)
+    }
+
+    def "produces reasonable error message when external script fails on compilation"() {
+        externalScript << 'import org.gradle()'
+
+        when:
+        fails()
+
+        then:
+        failure.assertHasDescription("Could not compile script '$externalScript'.")
+                .assertThatCause(containsString("script '${externalScript}': 1: unexpected token: ("))
+                .assertHasFileName("Script '$externalScript'")
+                .assertHasLineNumber(1)
+    }
+
+    def "reports missing script"() {
+        when:
+        fails()
+
+        then:
+        failure.assertHasDescription("A problem occurred evaluating root project 'project'.")
+                .assertHasCause("Could not read script '${externalScript}' as it does not exist.")
+                .assertHasFileName("Build file '${buildFile}'")
+                .assertHasLineNumber(2)
+    }
+
+    def "produces reasonable error message when task execution fails"() {
+        externalScript << '''
+task doStuff << {
+    throw new RuntimeException('fail')
+}
+'''
+        when:
+        fails 'doStuff'
+
+        then:
+        failure.assertHasDescription('Execution failed for task \':doStuff\'.')
+                .assertHasCause('fail')
+                .assertHasFileName("Script '${externalScript}'")
+                .assertHasLineNumber(3)
+    }
+}
+
diff --git a/subprojects/core/src/integTest/groovy/org/gradle/api/FinalizerTaskIntegrationTest.groovy b/subprojects/core/src/integTest/groovy/org/gradle/api/FinalizerTaskIntegrationTest.groovy
new file mode 100644
index 0000000..f9806f4
--- /dev/null
+++ b/subprojects/core/src/integTest/groovy/org/gradle/api/FinalizerTaskIntegrationTest.groovy
@@ -0,0 +1,248 @@
+/*
+ * Copyright 2013 the original author or authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF 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.integtests.fixtures.AbstractIntegrationSpec
+import org.gradle.util.TextUtil
+import spock.lang.Ignore
+import spock.lang.Unroll
+
+class FinalizerTaskIntegrationTest extends AbstractIntegrationSpec {
+    @Unroll
+    void 'finalizer tasks are scheduled as expected'() {
+        setupProject()
+
+        when:
+        succeeds(*requestedTasks)
+
+        then:
+        executedTasks == expectedExecutedTasks
+
+        where:
+        requestedTasks | expectedExecutedTasks
+        ['a']          | [':c', ':a', ':d', ':b']
+        ['a', 'b']     | [':c', ':a', ':d', ':b']
+        ['d', 'a']     | [':d', ':c', ':a', ':b']
+    }
+
+    @Unroll
+    void 'finalizer tasks work with task excluding'() {
+        setupProject()
+        executer.withArguments('-x', excludedTask)
+
+        tasksNotInGraph.each { task ->
+            buildFile << """
+                gradle.taskGraph.whenReady { graph ->
+                    assert !graph.hasTask('$task')
+                }
+            """
+        }
+
+        when:
+        succeeds 'a'
+
+        then:
+        executedTasks == expectedExecutedTasks
+
+        where:
+        excludedTask | expectedExecutedTasks
+        'b'          | [':c', ':a']
+        'd'          | [':c', ':a', ':b']
+        'a'          | []
+
+
+        tasksNotInGraph = [':a', ':b', ':c', ':d'] - expectedExecutedTasks
+    }
+
+    @Unroll
+    void 'finalizer tasks work with --continue'() {
+        setupProject()
+        executer.withArguments('--continue')
+
+        buildFile << """
+            ${failingTask}.doLast { throw new RuntimeException() }
+        """
+
+        when:
+        fails(*requestedTasks)
+
+        then:
+        executedTasks == expectedExecutedTasks
+
+        where:
+        requestedTasks | failingTask | expectedExecutedTasks
+        ['a']          | 'c'         | [':c']
+        ['a', 'b']     | 'a'         | [':c', ':a', ':d', ':b']
+        ['a', 'b']     | 'c'         | [':c', ':d', ':b']
+    }
+
+    @Ignore
+    @Unroll
+    void 'finalizer tasks work with task disabling'() {
+        setupProject()
+        buildFile << """
+            $taskDisablingStatement
+
+            gradle.taskGraph.whenReady { graph ->
+                assert [a, b, c, d].every { graph.hasTask(it) }
+            }
+        """
+
+        when:
+        succeeds 'a'
+
+        then:
+        executedTasks == [':c']
+
+        where:
+        taskDisablingStatement << ['a.enabled = false', 'a.onlyIf {false}']
+    }
+
+    @Ignore
+    void 'requesting to run finalizer task before finalized results in a circular dependency failure'() {
+        setupProject()
+
+        expect:
+        fails 'b', 'a'
+    }
+
+    void 'finalizer tasks are executed as expected in parallel builds'() {
+        setupMultipleProjects()
+        executer.withArguments('--parallel')
+
+        when:
+        succeeds 'a'
+
+        then:
+        executedTasks == [':a:c', ':a:a', ':b:d', ':b:b']
+    }
+
+    void 'finalizers for finalizers are executed when finalized is executed'() {
+        buildFile << """
+            task a {
+                finalizedBy 'b'
+            }
+            task b {
+                finalizedBy 'c'
+            }
+            task c
+        """
+
+        when:
+        succeeds 'a'
+
+        then:
+        executedTasks == [':a', ':b', ':c']
+    }
+
+    void 'finalizer tasks are executed after their dependencies'() {
+        buildFile << """
+            task a {
+                dependsOn 'b', 'c'
+            }
+            task b
+            task c {
+                finalizedBy 'b'
+            }
+        """
+
+        when:
+        succeeds 'a'
+
+        then:
+        executedTasks == [':c', ':b', ':a']
+    }
+
+    void 'circular dependency errors are detected for finalizer tasks'() {
+        buildFile << """
+            task a {
+                finalizedBy 'b'
+                dependsOn 'c'
+            }
+            task b
+            task c {
+                mustRunAfter 'b'
+            }
+        """
+
+        when:
+        fails 'a'
+
+        then:
+        failure.assertHasDescription TextUtil.toPlatformLineSeparators("""Circular dependency between the following tasks:
+:a
+\\--- :c
+     \\--- :b
+          \\--- :a (*)
+
+(*) - details omitted (listed previously)""")
+    }
+
+    void 'finalizer task can be used by multiple tasks that depend on one another'(){
+        buildFile << """
+            task a {
+                finalizedBy 'c'
+            }
+            task b {
+                dependsOn 'a'
+                finalizedBy 'c'
+            }
+            task c
+        """
+
+        when:
+        succeeds 'b'
+
+        then:
+        executedTasks == [':a', ':b', ':c']
+    }
+
+    private void setupProject() {
+        buildFile << """
+            task a {
+                finalizedBy 'b'
+                dependsOn 'c'
+            }
+            task b {
+                dependsOn 'd'
+            }
+            task c
+            task d
+        """
+    }
+
+    private void setupMultipleProjects() {
+        settingsFile << """
+            include 'a', 'b'
+        """
+
+        file('a/build.gradle') << """
+            task a {
+                finalizedBy ':b:b'
+                dependsOn 'c'
+            }
+            task c
+        """
+
+        file('b/build.gradle') << """
+            task b {
+                dependsOn 'd'
+            }
+            task d
+        """
+    }
+}
diff --git a/subprojects/core/src/integTest/groovy/org/gradle/api/GradlePluginIntegrationTest.groovy b/subprojects/core/src/integTest/groovy/org/gradle/api/GradlePluginIntegrationTest.groovy
new file mode 100644
index 0000000..01c22e8
--- /dev/null
+++ b/subprojects/core/src/integTest/groovy/org/gradle/api/GradlePluginIntegrationTest.groovy
@@ -0,0 +1,97 @@
+/*
+ * Copyright 2013 the original author or authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF 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.integtests.fixtures.AbstractIntegrationSpec
+
+class GradlePluginIntegrationTest extends AbstractIntegrationSpec {
+    File initFile;
+
+    def setup() {
+        initFile = temporaryFolder.createFile("initscripts/init.gradle")
+        executer.usingInitScript(initFile);
+    }
+
+    def "can apply binary plugin from init script"() {
+        when:
+        initFile << """
+        apply plugin:SimpleGradlePlugin
+
+        class SimpleGradlePlugin implements Plugin<Gradle> {
+            void apply(Gradle aGradle) {
+                aGradle.buildFinished {
+                    println "Gradle Plugin received build finished!"
+                }
+            }
+        }
+        """
+        then:
+        def executed = succeeds('tasks')
+        executed.output.contains("Gradle Plugin received build finished!")
+    }
+
+    def "can apply script with relative path"() {
+        setup:
+        def externalInitFile = temporaryFolder.createFile("initscripts/somePath/anInit.gradle")
+        externalInitFile << """
+        buildFinished {
+            println "Gradle Plugin received build finished!"
+        }
+        """
+        when:
+        initFile << """
+        apply from: "somePath/anInit.gradle"
+        """
+        then:
+        def executed = succeeds('tasks')
+        executed.output.contains("Gradle Plugin received build finished!")
+    }
+
+    def "cannot apply script with relative path on Gradle instance"() {
+        when:
+        initFile << """
+            gradle.apply(from: "somePath/anInit.gradle")
+            """
+        then:
+        fails('tasks')
+        failure.assertHasCause("Cannot convert relative path somePath${File.separator}anInit.gradle to an absolute file")
+    }
+
+    def "path to script is interpreted relative to the applying script"() {
+        setup:
+        def externalInitFile = temporaryFolder.createFile("initscripts/path1/anInit.gradle")
+        externalInitFile << """
+            buildFinished {
+                println "Gradle Plugin received build finished!"
+            }
+        """
+        def anotherExternalInitFile = temporaryFolder.createFile("initscripts/path2/anotherInit.gradle")
+        anotherExternalInitFile << """
+            apply from: '../path1/anInit.gradle'
+            """
+
+        when:
+        initFile << """
+            apply from: "path2/anotherInit.gradle"
+            """
+        then:
+        def executed = succeeds('tasks')
+        executed.output.contains("Gradle Plugin received build finished!")
+    }
+}
\ No newline at end of file
diff --git a/subprojects/core/src/integTest/groovy/org/gradle/api/InitScriptErrorIntegrationTest.groovy b/subprojects/core/src/integTest/groovy/org/gradle/api/InitScriptErrorIntegrationTest.groovy
new file mode 100644
index 0000000..3d1f7d6
--- /dev/null
+++ b/subprojects/core/src/integTest/groovy/org/gradle/api/InitScriptErrorIntegrationTest.groovy
@@ -0,0 +1,58 @@
+/*
+ * Copyright 2013 the original author or authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF 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.integtests.fixtures.AbstractIntegrationSpec
+
+import static org.hamcrest.Matchers.containsString
+
+public class InitScriptErrorIntegrationTest extends AbstractIntegrationSpec {
+    def initScript
+
+    def "setup"() {
+        initScript = file('init.gradle')
+        executer.usingInitScript(initScript)
+    }
+
+    def "produces reasonable error message when init script evaluation fails with GroovyException"() {
+        initScript << """
+    createTakk('do-stuff')
+"""
+        when:
+        fails()
+
+        then:
+        failure.assertHasDescription("A problem occurred evaluating initialization script.")
+                .assertHasCause("Could not find method createTakk() for arguments [do-stuff] on build.")
+                .assertHasFileName("Initialization script '$initScript'")
+                .assertHasLineNumber(2)
+    }
+
+    def "produces reasonable error message when init script compilation fails"() {
+        initScript << """
+    // a comment
+    import org.gradle.unknown.Unknown
+    new Unknown()
+"""
+        when:
+        fails()
+
+        then:
+        failure.assertHasDescription("Could not compile initialization script '$initScript'.")
+                .assertThatCause(containsString("initialization script '$initScript': 3: unable to resolve class org.gradle.unknown.Unknown"))
+                .assertHasFileName("Initialization script '$initScript'")
+                .assertHasLineNumber(3)
+    }
+}
diff --git a/subprojects/core/src/integTest/groovy/org/gradle/api/ProfilingIntegrationTest.groovy b/subprojects/core/src/integTest/groovy/org/gradle/api/ProfilingIntegrationTest.groovy
new file mode 100644
index 0000000..0f301ca
--- /dev/null
+++ b/subprojects/core/src/integTest/groovy/org/gradle/api/ProfilingIntegrationTest.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
+
+import org.gradle.integtests.fixtures.AbstractIntegrationSpec
+import org.jsoup.Jsoup
+import org.jsoup.nodes.Document
+
+class ProfilingIntegrationTest extends AbstractIntegrationSpec {
+
+    def "can generate profiling report"() {
+        file('settings.gradle') << 'include "a", "b", "c"'
+        buildFile << '''
+allprojects {
+    apply plugin: 'java'
+    task fooTask
+    task barTask
+}
+'''
+        when:
+        executer.withArguments("--profile").withTasks("build", "fooTask", "-x", "barTask").run()
+
+        then:
+        def reportFile = file('build/reports/profile').listFiles().find { it.name ==~ /profile-.+.html/ }
+        Document document = Jsoup.parse(reportFile, null);
+        !document.select("TD:contains(:jar)").isEmpty()
+        !document.select("TD:contains(:a:jar)").isEmpty()
+        !document.select("TD:contains(:b:jar)").isEmpty()
+        !document.select("TD:contains(:c:jar)").isEmpty()
+        document.text().contains("build fooTask")
+        document.text().contains("-x barTask")
+    }
+}
\ No newline at end of file
diff --git a/subprojects/core/src/integTest/groovy/org/gradle/api/ProjectConfigurationIntegrationTest.groovy b/subprojects/core/src/integTest/groovy/org/gradle/api/ProjectConfigurationIntegrationTest.groovy
index 88b916a..adc2e86 100644
--- a/subprojects/core/src/integTest/groovy/org/gradle/api/ProjectConfigurationIntegrationTest.groovy
+++ b/subprojects/core/src/integTest/groovy/org/gradle/api/ProjectConfigurationIntegrationTest.groovy
@@ -18,9 +18,6 @@ package org.gradle.api
 
 import org.gradle.integtests.fixtures.AbstractIntegrationSpec
 
-/**
- * by Szczepan Faber, created at: 11/21/12
- */
 class ProjectConfigurationIntegrationTest extends AbstractIntegrationSpec {
 
     def "accessing the task by path from containing project is safe"() {
diff --git a/subprojects/core/src/integTest/groovy/org/gradle/api/ProjectConfigureEventsErrorIntegrationTest.groovy b/subprojects/core/src/integTest/groovy/org/gradle/api/ProjectConfigureEventsErrorIntegrationTest.groovy
new file mode 100755
index 0000000..66ffa9a
--- /dev/null
+++ b/subprojects/core/src/integTest/groovy/org/gradle/api/ProjectConfigureEventsErrorIntegrationTest.groovy
@@ -0,0 +1,114 @@
+/*
+ * Copyright 2013 the original author or authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF 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.integtests.fixtures.AbstractIntegrationSpec
+import spock.lang.Ignore
+
+public class ProjectConfigureEventsErrorIntegrationTest extends AbstractIntegrationSpec {
+
+    def "setup"() {
+        settingsFile << "rootProject.name = 'projectConfigure'"
+    }
+
+    def "produces reasonable error message when beforeProject action fails"() {
+        when:
+        settingsFile << """
+    gradle.beforeProject {
+        throw new RuntimeException("beforeProject failure")
+    }
+"""
+        buildFile << """
+    task test
+"""
+        then:
+        fails('test')
+        failure.assertHasDescription("A problem occurred configuring root project 'projectConfigure'.")
+                .assertHasCause("beforeProject failure")
+                .assertHasFileName("Settings file '${settingsFile.path}'")
+                .assertHasLineNumber(3)
+    }
+
+    def "produces reasonable error message when afterProject action fails"() {
+        when:
+        settingsFile << """
+    gradle.afterProject {
+        throw new RuntimeException("afterProject failure")
+    }
+"""
+        buildFile << """
+    task test
+"""
+        then:
+        fails('test')
+        failure.assertHasDescription("A problem occurred configuring root project 'projectConfigure'.")
+                .assertHasCause("afterProject failure")
+                .assertHasFileName("Settings file '${settingsFile.path}'")
+                .assertHasLineNumber(3)
+    }
+
+    def "produces reasonable error message when afterEvaluate action fails"() {
+        when:
+        buildFile << """
+    project.afterEvaluate {
+        throw new RuntimeException("afterEvaluate failure")
+    }
+    task test
+"""
+        then:
+        fails('test')
+        failure.assertHasDescription("A problem occurred configuring root project 'projectConfigure'.")
+                .assertHasCause("afterEvaluate failure")
+                .assertHasFileName("Build file '${buildFile.path}'")
+                .assertHasLineNumber(3)
+    }
+
+    def "produces reasonable error message when taskGraph.whenReady action fails"() {
+        buildFile << """
+    gradle.taskGraph.whenReady {
+        throw new RuntimeException('broken closure')
+    }
+    task a
+"""
+
+        when:
+        fails()
+
+        then:
+        failure.assertHasDescription("broken closure")
+                .assertHasNoCause()
+                .assertHasFileName("Build file '$buildFile'")
+                .assertHasLineNumber(3);
+    }
+
+    @Ignore
+    def "produces reasonable error message when task dependency closure throws exception"() {
+        buildFile << """
+    task a
+    a.dependsOn {
+        throw new RuntimeException('broken')
+    }
+"""
+        when:
+        fails "a"
+
+        then:
+        failure.assertHasDescription("Could not determine the dependencies of task ':a'.")
+                .assertHasCause('broken')
+                .assertHasFileName("Build file '$buildFile'")
+                .assertHasLineNumber(4)
+    }
+}
diff --git a/subprojects/core/src/integTest/groovy/org/gradle/api/SettingsPluginIntegrationSpec.groovy b/subprojects/core/src/integTest/groovy/org/gradle/api/SettingsPluginIntegrationSpec.groovy
new file mode 100644
index 0000000..d67aafa
--- /dev/null
+++ b/subprojects/core/src/integTest/groovy/org/gradle/api/SettingsPluginIntegrationSpec.groovy
@@ -0,0 +1,83 @@
+/*
+ * Copyright 2013 the original author or authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF 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.integtests.fixtures.AbstractIntegrationSpec
+import org.gradle.test.fixtures.file.TestFile
+
+class SettingsPluginIntegrationSpec extends AbstractIntegrationSpec {
+
+    def setup(){
+        executer.usingSettingsFile(settingsFile)
+        settingsFile << "rootProject.projectDir = file('..')\n"
+    }
+
+    def "can apply plugin class from settings.gradle"() {
+        when:
+        settingsFile << """
+        apply plugin: SimpleSettingsPlugin
+
+        class SimpleSettingsPlugin implements Plugin<Settings> {
+            void apply(Settings mySettings) {
+                mySettings.include("moduleA");
+            }
+        }
+        """
+
+        then:
+        succeeds(':moduleA:dependencies')
+    }
+
+    def "can apply plugin class from buildSrc"() {
+        setup:
+        file("settings/buildSrc/src/main/java/test/SimpleSettingsPlugin.java").createFile().text = """
+            package test;
+
+            import org.gradle.api.Plugin;
+            import org.gradle.api.initialization.Settings;
+
+            public class SimpleSettingsPlugin implements Plugin<Settings> {
+                public void apply(Settings settings) {
+                    settings.include(new String[]{"moduleA"});
+                }
+            }
+
+            """
+
+        when:
+        settingsFile << "apply plugin: test.SimpleSettingsPlugin"
+
+        then:
+        succeeds(':moduleA:dependencies')
+    }
+
+    def "can apply script with relative path"() {
+        setup:
+        testDirectory.createFile("settings/somePath/settingsPlugin.gradle") << "apply from: 'path2/settings.gradle'";
+        testDirectory.createFile("settings/somePath/path2/settings.gradle") << "include 'moduleA'";
+
+        when:
+        settingsFile << "apply from: 'somePath/settingsPlugin.gradle'"
+
+        then:
+        succeeds(':moduleA:dependencies')
+    }
+
+    protected TestFile getSettingsFile() {
+        testDirectory.file('settings/settings.gradle')
+    }
+}
diff --git a/subprojects/core/src/integTest/groovy/org/gradle/api/SettingsScriptErrorIntegrationTest.groovy b/subprojects/core/src/integTest/groovy/org/gradle/api/SettingsScriptErrorIntegrationTest.groovy
new file mode 100755
index 0000000..e28304f
--- /dev/null
+++ b/subprojects/core/src/integTest/groovy/org/gradle/api/SettingsScriptErrorIntegrationTest.groovy
@@ -0,0 +1,41 @@
+/*
+ * Copyright 2013 the original author or authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF 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.integtests.fixtures.AbstractIntegrationSpec
+
+public class SettingsScriptErrorIntegrationTest extends AbstractIntegrationSpec {
+
+    def "setup"() {
+        settingsFile << "rootProject.name = 'ProjectError'"
+    }
+
+    def "produces reasonable error message when settings file evaluation fails"() {
+        settingsFile << """
+
+throw new RuntimeException('<failure message>')
+"""
+        when:
+        fails()
+
+        then:
+        failure.assertHasDescription("A problem occurred evaluating settings 'ProjectError'.")
+                .assertHasCause("<failure message>")
+                .assertHasFileName("Settings file '$settingsFile'")
+                .assertHasLineNumber(3)
+
+    }
+}
diff --git a/subprojects/core/src/integTest/groovy/org/gradle/api/dsl/ConcurrentClassDecorationSpec.groovy b/subprojects/core/src/integTest/groovy/org/gradle/api/dsl/ConcurrentClassDecorationSpec.groovy
new file mode 100644
index 0000000..836b0b8
--- /dev/null
+++ b/subprojects/core/src/integTest/groovy/org/gradle/api/dsl/ConcurrentClassDecorationSpec.groovy
@@ -0,0 +1,48 @@
+/*
+ * Copyright 2013 the original author or authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF 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
+
+import org.gradle.api.plugins.ExtensionAware
+import org.gradle.integtests.fixtures.AbstractIntegrationSpec
+import org.gradle.internal.reflect.Instantiator
+import spock.lang.Issue
+
+class ConcurrentClassDecorationSpec extends AbstractIntegrationSpec {
+
+    @Issue("http://issues.gradle.org/browse/GRADLE-2836")
+    def "can decorate classes concurrently"() {
+        given:
+        file("buildSrc/src/main/java/Thing.java") << "public class Thing {}"
+        ("a".."d").each { name ->
+            settingsFile << "include '$name'\n"
+            file("$name/build.gradle") << """
+                task decorateClass << {
+                    def instantiator = project.services.get(${Instantiator.name})
+                    def thing = instantiator.newInstance(Thing)
+                    assert thing instanceof ${ExtensionAware.name}
+                }
+            """
+        }
+
+        when:
+        args "--parallel"
+
+        then:
+        succeeds "decorateClass"
+    }
+
+}
diff --git a/subprojects/core/src/integTest/groovy/org/gradle/api/tasks/ArchiveIntegrationTest.groovy b/subprojects/core/src/integTest/groovy/org/gradle/api/tasks/ArchiveIntegrationTest.groovy
index 6aa4069..9637d29 100644
--- a/subprojects/core/src/integTest/groovy/org/gradle/api/tasks/ArchiveIntegrationTest.groovy
+++ b/subprojects/core/src/integTest/groovy/org/gradle/api/tasks/ArchiveIntegrationTest.groovy
@@ -13,16 +13,12 @@
  * See the License for the specific language governing permissions and
  * limitations under the License.
  */
-
-
-
-
 package org.gradle.api.tasks
 
 import org.apache.commons.lang.RandomStringUtils
-import org.apache.commons.lang.StringUtils
 import org.gradle.integtests.fixtures.AbstractIntegrationSpec
 import org.gradle.integtests.fixtures.TestResources
+import org.gradle.test.fixtures.archive.TarTestFixture
 import org.gradle.test.fixtures.file.TestFile
 import org.junit.Rule
 
@@ -71,8 +67,6 @@ public class ArchiveIntegrationTest extends AbstractIntegrationSpec {
         file('build/test.tar').assertDoesNotExist()
     }
 
-
-
     def canCopyFromATar() {
         given:
         createTar('test.tar') {
@@ -222,7 +216,8 @@ public class ArchiveIntegrationTest extends AbstractIntegrationSpec {
         file('dest').assertHasDescendants('someDir/1.txt')
     }
 
-    @Rule public final TestResources resources = new TestResources(temporaryFolder)
+    @Rule
+    public final TestResources resources = new TestResources(temporaryFolder)
 
     def "tarTreeFailsGracefully"() {
         given:
@@ -344,11 +339,11 @@ public class ArchiveIntegrationTest extends AbstractIntegrationSpec {
         run 'uncompressedZip'
         run 'compressedZip'
         then:
-	def uncompressedSize = file('build/uncompressedTest.zip').length()
-	def compressedSize = file('build/compressedTest.zip').length()
-	println "uncompressed" + uncompressedSize
-	println "compressed" + compressedSize
-    assert compressedSize < uncompressedSize
+        def uncompressedSize = file('build/uncompressedTest.zip').length()
+        def compressedSize = file('build/compressedTest.zip').length()
+        println "uncompressed" + uncompressedSize
+        println "compressed" + compressedSize
+        assert compressedSize < uncompressedSize
 
         def expandDir = file('expandedUncompressed')
         file('build/uncompressedTest.zip').unzipTo(expandDir)
@@ -598,10 +593,67 @@ public class ArchiveIntegrationTest extends AbstractIntegrationSpec {
         expandDir.assertHasDescendants('shared/zip.txt', 'zipdir1/file1.txt', 'shared/tar.txt', 'tardir1/file1.txt', 'shared/dir.txt', 'dir1/file1.txt')
     }
 
-    def createTar(String name, Closure cl) {
+
+    def ensureDuplicatesIncludedInTarByDefault() {
+        given:
+        createFilesStructureForDupeTests();
+        buildFile << '''
+            task tar(type: Tar) {
+                from 'dir1'
+                from 'dir2'
+                from 'dir3'
+                destinationDir = buildDir
+                archiveName = 'test.tar'
+            }
+            '''
+        when:
+        run 'tar'
+
+        then:
+        def tar = new TarTestFixture(file("build/test.tar"))
+        tar.assertContainsFile('file1.txt', 2)
+        tar.assertContainsFile('file2.txt')
+    }
+
+    def ensureDuplicatesCanBeExcludedFromTar() {
+        given:
+        createFilesStructureForDupeTests()
+        buildFile << '''
+            task tar(type: Tar) {
+                from 'dir1'
+                from 'dir2'
+                from 'dir3'
+                destinationDir = buildDir
+                archiveName = 'test.tar'
+                eachFile { it.duplicatesStrategy = 'exclude' }
+            }
+            '''
+        when:
+        run 'tar'
+
+        then:
+        def tar = new TarTestFixture(file("build/test.tar"))
+        tar.assertContainsFile('file1.txt')
+        tar.assertContainsFile('file2.txt')
+        tar.content("file1.txt") == "dir1/file1.txt"
+    }
+
+    private def createTar(String name, Closure cl) {
         TestFile tarRoot = file("${name}.root")
         TestFile tar = file(name)
         tarRoot.create(cl)
         tarRoot.tarTo(tar)
     }
+
+    private def createFilesStructureForDupeTests() {
+        createDir('dir1', {
+            file('file1.txt').text = "dir1/file1.txt"
+        })
+        createDir('dir2', {
+            file 'file2.txt'
+        })
+        createDir('dir3', {
+            file('file1.txt').text = "dir3/file1.txt"
+        })
+    }
 }
diff --git a/subprojects/core/src/integTest/groovy/org/gradle/api/tasks/CopyTaskIntegrationSpec.groovy b/subprojects/core/src/integTest/groovy/org/gradle/api/tasks/CopyTaskIntegrationSpec.groovy
index afb84fb..5ee38a9 100644
--- a/subprojects/core/src/integTest/groovy/org/gradle/api/tasks/CopyTaskIntegrationSpec.groovy
+++ b/subprojects/core/src/integTest/groovy/org/gradle/api/tasks/CopyTaskIntegrationSpec.groovy
@@ -16,6 +16,7 @@
 
 package org.gradle.api.tasks
 
+import org.gradle.api.plugins.ExtensionAware
 import org.gradle.integtests.fixtures.AbstractIntegrationSpec
 import spock.lang.Issue
 
@@ -67,4 +68,92 @@ class CopyTaskIntegrationSpec extends AbstractIntegrationSpec {
         then:
         file("build/resources", weirdFileName).exists()
     }
+
+    def "nested specs and details arent extensible objects"() {
+        given:
+        file("a/a.txt").touch()
+
+        buildScript """
+            task copy(type: Copy) {
+                assert delegate instanceof ${ExtensionAware.name}
+                into "out"
+                from "a", {
+                    assert !(delegate instanceof ${ExtensionAware.name})
+                    eachFile {
+                        it.name = "rename"
+                        assert !(delegate instanceof ${ExtensionAware.name})
+                    }
+                }
+            }
+        """
+
+        when:
+        succeeds "copy"
+
+        then:
+        file("out/rename").exists()
+    }
+
+    @Issue("http://issues.gradle.org/browse/GRADLE-2838")
+    def "include empty dirs works when nested"() {
+        given:
+        file("a/a.txt") << "foo"
+        file("a/dirA").createDir()
+        file("b/b.txt") << "foo"
+        file("b/dirB").createDir()
+
+        buildScript """
+            task copyTask(type: Copy) {
+                into "out"
+                from "b", {
+                    includeEmptyDirs = false
+                }
+                from "a"
+                from "c", {}
+            }
+        """
+
+        when:
+        succeeds "copyTask"
+
+        then:
+        ":copyTask" in nonSkippedTasks
+        def destinationDir = file("out")
+        destinationDir.assertHasDescendants("a.txt", "b.txt")
+        destinationDir.listFiles().findAll { it.directory }*.name.toSet() == ["dirA"].toSet()
+    }
+
+    def "include empty dirs is overridden by subsequent"() {
+        given:
+        file("a/a.txt") << "foo"
+        file("a/dirA").createDir()
+        file("b/b.txt") << "foo"
+        file("b/dirB").createDir()
+
+
+        buildScript """
+            task copyTask(type: Copy) {
+                into "out"
+                from "b", {
+                    includeEmptyDirs = false
+                }
+                from "a"
+                from "c", {}
+                from "b", {
+                    includeEmptyDirs = true
+                }
+            }
+        """
+
+        when:
+        succeeds "copyTask"
+
+        then:
+        ":copyTask" in nonSkippedTasks
+
+        def destinationDir = file("out")
+        destinationDir.assertHasDescendants("a.txt", "b.txt")
+        destinationDir.listFiles().findAll { it.directory }*.name.toSet() == ["dirA", "dirB"].toSet()
+    }
+
 }
diff --git a/subprojects/core/src/integTest/groovy/org/gradle/api/tasks/CopyTaskIntegrationTest.groovy b/subprojects/core/src/integTest/groovy/org/gradle/api/tasks/CopyTaskIntegrationTest.groovy
index 2c420e1..9d43b3d 100644
--- a/subprojects/core/src/integTest/groovy/org/gradle/api/tasks/CopyTaskIntegrationTest.groovy
+++ b/subprojects/core/src/integTest/groovy/org/gradle/api/tasks/CopyTaskIntegrationTest.groovy
@@ -18,11 +18,13 @@ package org.gradle.api.tasks
 import org.gradle.integtests.fixtures.AbstractIntegrationTest
 import org.gradle.integtests.fixtures.TestResources
 import org.gradle.test.fixtures.file.TestFile
+import org.gradle.util.Matchers
 import org.junit.Rule
 import org.junit.Test
 
 import static org.hamcrest.Matchers.equalTo
 import static org.hamcrest.Matchers.startsWith
+import static org.junit.Assert.assertFalse
 import static org.junit.Assert.assertThat
 
 public class CopyTaskIntegrationTest extends AbstractIntegrationTest {
@@ -435,4 +437,76 @@ public class CopyTaskIntegrationTest extends AbstractIntegrationTest {
         assert !file("dest", "emptyDir").exists()
         assert !file("dest", "yet", "another", "veryEmptyDir").exists()
     }
+
+    @Test
+    public void testCopyExcludeDuplicates() {
+        file('dir1', 'path', 'file.txt').createFile() << "f1"
+        file('dir2', 'path', 'file.txt').createFile() << "f2"
+
+
+        def buildFile = testFile('build.gradle') <<
+                '''
+            task copy(type: Copy) {
+                from 'dir1'
+                from 'dir2'
+                into 'dest'
+
+                eachFile { it.duplicatesStrategy = 'exclude' }
+            }
+
+        '''
+
+        def result = usingBuildFile(buildFile).withTasks("copy").run()
+        file('dest').assertHasDescendants('path/file.txt')
+        file('dest/path/file.txt').assertContents(Matchers.containsText("f1"))
+        assertFalse(result.output.contains('deprecated'))
+    }
+
+    @Test
+    public void renamedFileCanBeTreatedAsDuplicate() {
+        File file1 = file('dir1', 'path', 'file.txt').createFile()
+        File file2 = file('dir2', 'path', 'file2.txt').createFile()
+        file1.text = "file1"
+        file2.text = "file2"
+
+        def buildFile = testFile('build.gradle') <<
+                '''
+                task copy(type: Copy) {
+                    from 'dir1'
+                    from 'dir2'
+                    rename 'file2.txt', 'file.txt'
+                    into 'dest'
+
+                    eachFile { it.duplicatesStrategy = 'exclude' }
+                }
+            '''
+        def result = usingBuildFile(buildFile).withTasks("copy").run()
+        file('dest').assertHasDescendants('path/file.txt')
+        file('dest/path/file.txt').assertContents(Matchers.containsText("file1"))
+        assertFalse(result.output.contains('deprecated'))
+    }
+
+    @Test
+    public void testChainMatchingRules() {
+        file('path/abc.txt').createFile().write('test file with $attr')
+        file('path/bcd.txt').createFile()
+
+        def buildFile = testFile('build.gradle') <<
+            '''
+            task copy(type: Copy) {
+                from 'path'
+                into 'dest'
+                filesMatching ('**/a*') {
+                    path = path + '.template'
+                }
+                filesMatching ('**/*.template') {
+                    expand(attr: 'some value')
+                    path = path.replace('template', 'concrete')
+                }
+            }'''
+
+        usingBuildFile(buildFile).withTasks('copy').run();
+        file('dest').assertHasDescendants('bcd.txt', 'abc.txt.concrete')
+        file('dest/abc.txt.concrete').text = 'test file with some value'
+    }
 }
diff --git a/subprojects/core/src/integTest/groovy/org/gradle/api/tasks/TaskCommandLineConfigurationIntegrationSpec.groovy b/subprojects/core/src/integTest/groovy/org/gradle/api/tasks/TaskCommandLineConfigurationIntegrationSpec.groovy
new file mode 100644
index 0000000..6a2c0ae
--- /dev/null
+++ b/subprojects/core/src/integTest/groovy/org/gradle/api/tasks/TaskCommandLineConfigurationIntegrationSpec.groovy
@@ -0,0 +1,249 @@
+/*
+ * Copyright 2012 the original author or authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF 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.integtests.fixtures.AbstractIntegrationSpec
+import spock.lang.Ignore
+
+class TaskCommandLineConfigurationIntegrationSpec extends AbstractIntegrationSpec {
+
+    final String someConfigurableTaskType = """
+    import org.gradle.api.internal.tasks.CommandLineOption
+
+    class SomeTask extends DefaultTask {
+        boolean first
+        String second
+
+        @CommandLineOption(options = "first", description = "configures 'first' field")
+        void setFirst(boolean first) {
+            this.first = first
+        }
+
+        @CommandLineOption(options = "second", description = "configures 'second' field")
+        void setSecond(String second) {
+            this.second = second
+        }
+
+        //more stress
+        void setSecond(Object second) {
+            this.second = second.toString()
+        }
+
+        @TaskAction
+        void renderFields() {
+            println "first=" + first + ",second=" + second
+        }
+    }"""
+
+    def "can configure task from command line in multiple projects"() {
+        given:
+        file("settings.gradle") << "include 'project2'"
+        file("build.gradle") << """
+            allprojects {
+                task someTask(type: SomeTask)
+            }
+            task task1 //extra stress
+            task task2
+
+            $someConfigurableTaskType
+"""
+
+        when:
+        run 'someTask'
+
+        then:
+        output.contains 'first=false,second=null'
+
+        when:
+        run 'task1', 'someTask', '--first', '--second', 'hey buddy', 'task2'
+
+        then:
+        output.count('first=true,second=hey buddy') == 2
+        result.assertTasksExecuted(":task1", ":someTask", ":project2:someTask", ":task2")
+    }
+
+    def "tasks can be configured with different options"() {
+        given:
+        file("settings.gradle") << "include 'project2'"
+        file("build.gradle") << """
+            allprojects {
+                task someTask(type: SomeTask)
+            }
+
+            $someConfigurableTaskType
+"""
+
+        when:
+        run ':someTask', '--second', 'one', ':project2:someTask', '--second', 'two'
+
+        then:
+        result.assertTasksExecuted(":someTask", ":project2:someTask")
+        output.count('second=one') == 1
+        output.count('second=two') == 1
+    }
+
+    def "tasks are configured exclusively with their options"() {
+        given:
+        file("settings.gradle") << "include 'project2'"
+        file("build.gradle") << """
+            allprojects {
+                task someTask(type: SomeTask)
+            }
+
+            $someConfigurableTaskType
+"""
+
+        when:
+        run ':someTask', '--second', 'one', ':project2:someTask', '--first'
+
+        then:
+        result.assertTasksExecuted(":someTask", ":project2:someTask")
+        output.count('first=false,second=one') == 1 //--first flag was set only on the :project2:someTask
+        output.count('first=true,second=null') == 1 //--second option was set only on the :someTask
+    }
+
+    def "task name that matches command value is not included in execution"() {
+        given:
+        file("build.gradle") << """
+            task foo
+            task someTask(type: SomeTask)
+
+            $someConfigurableTaskType
+"""
+
+        when:
+        run 'someTask', '--second', 'foo'
+
+        then:
+        output.contains 'second=foo'
+        result.assertTasksExecuted(":someTask") //no 'foo' task
+    }
+
+    def "multiple different tasks configured at single command line"() {
+        given:
+        file("build.gradle") << """
+            task foo
+            task someTask(type: SomeTask)
+
+            $someConfigurableTaskType
+"""
+
+        when:
+        run 'someTask', '--second', 'foo', 'tasks', '--all'
+
+        then:
+        output.contains 'second=foo'
+        result.assertTasksExecuted(":someTask", ":tasks")
+    }
+
+    def "different tasks match name but only one accepts the option"() {
+        given:
+        file("settings.gradle") << "include 'other'"
+        file("build.gradle") << """
+            task someTask(type: SomeTask)
+            project(":other") {
+              task someTask
+            }
+
+            $someConfigurableTaskType
+"""
+
+        when:
+        def failure = runAndFail 'someTask', '--first'
+
+        then:
+        failure.assertHasDescription("Problem configuring task :other:someTask from command line. Unknown command-line option '--first'.")
+    }
+
+    def "using an unknown option yields decent error message"() {
+        given:
+        file("build.gradle") << """
+            task foo
+            task someTask(type: SomeTask)
+            task someTask2(type: SomeTask)
+
+            $someConfigurableTaskType
+"""
+
+        when:
+        runAndFail 'someTask', '--second', 'foo', 'someTask2', '--secon', 'bar'
+
+        then:
+        failure.assertHasDescription("Problem configuring task :someTask2 from command line. Unknown command-line option '--secon'.")
+
+        //TODO it's not fixable easily we would need to change some stuff in options parsing. See also ignored test method below.
+//        when:
+//        runAndFail 'someTask', '-second', 'foo'
+//
+//        then:
+//        failure.assertHasDescription("Problem configuring task :someTask from command line. Unknown command-line option '-second'.")
+
+        when:
+        runAndFail 'someTask', '--second'
+
+        then:
+        failure.assertHasDescription("Problem configuring task :someTask from command line. No argument was provided for command-line option '--second'.")
+
+        when:
+        runAndFail 'someTask', '--second', 'hey', '--second', 'buddy'
+
+        then:
+        failure.assertHasDescription("Problem configuring task :someTask from command line. Multiple arguments were provided for command-line option '--second'.")
+    }
+
+    def "single dash user error yields decent error message"() {
+        when:
+        runAndFail 'tasks', '-all'
+
+        then:
+        failure.assertHasDescription("Problem configuring task :tasks from command line. Unknown command-line option '-l'.")
+    }
+
+    @Ignore
+    //more work & design decisions needed
+    def "single dash error is detected in the subsequent option"() {
+        given:
+        file("build.gradle") << """
+            task someTask(type: SomeTask)
+
+            $someConfigurableTaskType
+"""
+
+        when:
+        runAndFail 'someTask', '--first', '-second', 'foo'
+
+        then:
+        failure.assertHasDescription("Incorrect command line arguments: [-l, -l]. Task options require double dash, for example: 'gradle tasks --all'.")
+    }
+
+    @Ignore
+    //some existing problems with command line interface
+    def "unfriendly behavior of command line parsing"() {
+        when:
+        run '-all'
+
+        then:
+        "should fail with a decent error, not internal error (applies to all CommandLineArgumentExceptions)"
+        "should complain that there's no '-all' option"
+
+        when:
+        run 'tasks', '-refresh-dependenciess'
+
+        then:
+        "should fail in a consistent way as with '--refresh-dependenciess'"
+    }
+}
diff --git a/subprojects/core/src/integTest/groovy/org/gradle/api/tasks/bundling/ZipIntegrationTest.groovy b/subprojects/core/src/integTest/groovy/org/gradle/api/tasks/bundling/ZipIntegrationTest.groovy
new file mode 100644
index 0000000..b421456
--- /dev/null
+++ b/subprojects/core/src/integTest/groovy/org/gradle/api/tasks/bundling/ZipIntegrationTest.groovy
@@ -0,0 +1,100 @@
+/*
+ * Copyright 2013 the original author or authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.gradle.api.tasks.bundling
+
+import org.gradle.integtests.fixtures.AbstractIntegrationSpec
+import org.gradle.test.fixtures.archive.ZipTestFixture
+
+class ZipIntegrationTest extends AbstractIntegrationSpec {
+
+    def ensureDuplicatesIncludedWithoutWarning() {
+        given:
+        createTestFiles()
+        buildFile << '''
+            task zip(type: Zip) {
+                from 'dir1'
+                from 'dir2'
+                from 'dir3'
+                destinationDir = buildDir
+                archiveName = 'test.zip'
+            }
+            '''
+        when:
+        run 'zip'
+
+        then:
+        def theZip = new ZipTestFixture(file('build/test.zip'))
+        theZip.hasDescendants('file1.txt', 'file1.txt', 'file2.txt')
+    }
+
+    def ensureDuplicatesCanBeExcluded() {
+        given:
+        createTestFiles()
+        buildFile << '''
+            task zip(type: Zip) {
+                from 'dir1'
+                from 'dir2'
+                from 'dir3'
+                destinationDir = buildDir
+                archiveName = 'test.zip'
+                eachFile { it.duplicatesStrategy = 'exclude' }
+            }
+            '''
+        when:
+        run 'zip'
+
+        then:
+        def theZip = new ZipTestFixture(file('build/test.zip'))
+        theZip.hasDescendants('file1.txt', 'file2.txt')
+    }
+
+    def renamedFileWillBeTreatedAsDuplicateZip() {
+        given:
+        createTestFiles()
+        buildFile << '''
+                task zip(type: Zip) {
+                    from 'dir1'
+                    from 'dir2'
+                    destinationDir = buildDir
+                    rename 'file2.txt', 'file1.txt'
+                    archiveName = 'test.zip'
+                    eachFile { it.duplicatesStrategy = 'exclude' }
+                }
+                '''
+        when:
+        run 'zip'
+
+        then:
+        def theZip = new ZipTestFixture(file('build/test.zip'))
+        theZip.hasDescendants('file1.txt')
+        theZip.assertFileContent('file1.txt', "dir1/file1.txt")
+    }
+
+
+    private def createTestFiles() {
+        createDir('dir1', {
+            file('file1.txt').text = "dir1/file1.txt"
+        })
+        createDir('dir2', {
+            file('file2.txt').text = "dir2/file2.txt"
+        })
+        createDir('dir3', {
+            file('file1.txt').text = "dir3/file1.txt"
+        })
+    }
+
+}
diff --git a/subprojects/core/src/integTest/groovy/org/gradle/process/internal/PathLimitationIntegTest.groovy b/subprojects/core/src/integTest/groovy/org/gradle/process/internal/PathLimitationIntegTest.groovy
new file mode 100644
index 0000000..b82d3c1
--- /dev/null
+++ b/subprojects/core/src/integTest/groovy/org/gradle/process/internal/PathLimitationIntegTest.groovy
@@ -0,0 +1,277 @@
+/*
+ * Copyright 2013 the original author or authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS 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.apache.commons.lang.RandomStringUtils
+import org.gradle.CacheUsage
+import org.gradle.api.Action
+import org.gradle.api.internal.ClassPathRegistry
+import org.gradle.api.internal.DefaultClassPathProvider
+import org.gradle.api.internal.DefaultClassPathRegistry
+import org.gradle.api.internal.classpath.DefaultModuleRegistry
+import org.gradle.api.internal.classpath.ModuleRegistry
+import org.gradle.api.internal.file.TestFiles
+import org.gradle.api.logging.LogLevel
+import org.gradle.cache.CacheRepository
+import org.gradle.cache.internal.*
+import org.gradle.cache.internal.locklistener.NoOpFileLockContentionHandler
+import org.gradle.internal.id.LongIdGenerator
+import org.gradle.internal.jvm.Jvm
+import org.gradle.internal.nativeplatform.services.NativeServices
+import org.gradle.internal.os.OperatingSystem
+import org.gradle.listener.ListenerBroadcast
+import org.gradle.messaging.dispatch.Dispatch
+import org.gradle.messaging.dispatch.MethodInvocation
+import org.gradle.messaging.remote.MessagingServer
+import org.gradle.messaging.remote.ObjectConnection
+import org.gradle.messaging.remote.internal.MessagingServices
+import org.gradle.process.internal.child.WorkerProcessClassPathProvider
+import org.gradle.test.fixtures.file.TestFile
+import org.gradle.test.fixtures.file.TestNameTestDirectoryProvider
+import org.junit.Rule
+import spock.lang.Ignore
+import spock.lang.IgnoreIf
+import spock.lang.Specification
+import spock.lang.Unroll
+
+import static org.junit.Assert.assertFalse
+import static org.junit.Assert.assertTrue
+
+class PathLimitationIntegTest extends Specification {
+    private final TestListenerInterface listenerMock = Mock(TestListenerInterface.class);
+    private final MessagingServices messagingServices = new MessagingServices(getClass().getClassLoader());
+    private final MessagingServer server = messagingServices.get(MessagingServer.class);
+
+    @Rule
+    public final TestNameTestDirectoryProvider tmpDir = new TestNameTestDirectoryProvider();
+    private final ProcessMetaDataProvider metaDataProvider = new DefaultProcessMetaDataProvider(NativeServices.getInstance().get(org.gradle.internal.nativeplatform.ProcessEnvironment.class));
+    private final CacheFactory factory = new DefaultCacheFactory(new DefaultFileLockManager(metaDataProvider, new NoOpFileLockContentionHandler())).create();
+    private final CacheRepository cacheRepository = new DefaultCacheRepository(tmpDir.getTestDirectory(), null, CacheUsage.ON, factory);
+    private final ModuleRegistry moduleRegistry = new DefaultModuleRegistry();
+    private final ClassPathRegistry classPathRegistry = new DefaultClassPathRegistry(new DefaultClassPathProvider(moduleRegistry), new WorkerProcessClassPathProvider(cacheRepository, moduleRegistry));
+    private final DefaultWorkerProcessFactory workerFactory = new DefaultWorkerProcessFactory(LogLevel.INFO, server, classPathRegistry, TestFiles.resolver(tmpDir.getTestDirectory()), new LongIdGenerator());
+    private final ListenerBroadcast<TestListenerInterface> broadcast = new ListenerBroadcast<TestListenerInterface>(
+            TestListenerInterface.class);
+    private final RemoteExceptionListener exceptionListener = new RemoteExceptionListener(broadcast);
+
+    public void setup() {
+        broadcast.add(listenerMock);
+    }
+
+    public void after() {
+        messagingServices.stop();
+    }
+
+    @IgnoreIf({OperatingSystem.current().isWindows()})
+    @Unroll
+    def "WorkerProcessBuilder handles workingDir with absolute path length #absolutePathLength"() throws Throwable {
+        when:
+        def testWorkingDir = generateTestWorkingDirectory(absolutePathLength)
+        then:
+        assert testWorkingDir.exists()
+        execute(worker(new HelloWorldRemoteProcess(), testWorkingDir))
+        where:
+        absolutePathLength << [258, 259, 260]
+    }
+
+    @IgnoreIf({OperatingSystem.current().isWindows()})
+    @Unroll
+    def "JavaProcessBuilder handles workingDir with absolute path length #absolutePathLength"() throws Throwable {
+        when:
+        def testWorkingDir = generateTestWorkingDirectory(absolutePathLength)
+
+        assert testWorkingDir.exists()
+
+        ProcessBuilder processBuilder = new ProcessBuilder();
+        processBuilder.directory(testWorkingDir)
+
+        processBuilder.command(Jvm.current().getJavaExecutable().absolutePath, "-version")
+        then:
+        Process process = processBuilder.start()
+
+        process.waitFor() == 0
+        process.exitValue() == 0
+        and:
+        process.errorStream.text.contains("java version")
+
+        where:
+        absolutePathLength << [258, 259, 260]
+    }
+
+    @Ignore
+    @Unroll
+    def "OS handles workingDir with absolute path length #absolutePathLength"() throws Throwable {
+        setup:
+        def testWorkingDir = generateTestWorkingDirectory(absolutePathLength)
+        TestFile testBatchScript = tmpDir.getTestDirectory().createFile("testBatch.cmd")
+        testBatchScript.text = """
+        cd ${testWorkingDir.name}
+        java -version > o.txt 2>&1
+"""
+
+        when:
+
+        assert testWorkingDir.exists()
+        ProcessBuilder processBuilder = new ProcessBuilder();
+        processBuilder.directory(tmpDir.getTestDirectory())
+        processBuilder.command("CMD", "/C", "testBatch.cmd")
+
+        and:
+        Process process = processBuilder.start()
+
+        then:
+        process.waitFor() == 0
+        process.exitValue() == 0
+        and:
+        def outputText = new File(testWorkingDir, "o.txt").text
+        println outputText
+        assert outputText.contains("java version")
+        where:
+        absolutePathLength << [250, 255, 260] // 250 succeeds
+                                              // 255 fails because path + "/o.txt" >= 260
+                                              // 260 fails different because path >= 260
+    }
+
+
+    TestFile generateTestWorkingDirectory(int absolutePathLength) {
+        // windows can handle a path up to 260 characters (259 + NUL)
+        // we create a path that is 260 +1 (absolutePathLength + "/" + randompath)
+        def testDirectory = tmpDir.getTestDirectory()
+        def pathoffset = absolutePathLength - 1 - testDirectory.getAbsolutePath().length()
+        def alphanumeric = RandomStringUtils.randomAlphanumeric(pathoffset)
+        return testDirectory.createDir("$alphanumeric")
+    }
+
+    public static class HelloWorldRemoteProcess implements Action<WorkerProcessContext>, Serializable {
+        public void execute(WorkerProcessContext workerProcessContext) {
+            println "hello World!"
+        }
+    }
+
+    private ChildProcess worker(Action<? super WorkerProcessContext> action, File workingDirectory = tmpDir.getTestDirectory()) {
+
+        return new ChildProcess(action, workingDirectory);
+    }
+
+    void execute(ChildProcess... processes) throws Throwable {
+        for (ChildProcess process : processes) {
+            process.start();
+        }
+        for (ChildProcess process : processes) {
+            process.waitForStop();
+        }
+        messagingServices.stop();
+        exceptionListener.rethrow();
+    }
+
+    private class ChildProcess {
+        private boolean stopFails;
+        private boolean startFails;
+        private WorkerProcess proc;
+        private Action<? super WorkerProcessContext> action;
+        private List<String> jvmArgs = Collections.emptyList();
+        private Action<ObjectConnection> serverAction;
+        private final File workingDirectory
+
+        public ChildProcess(Action<? super WorkerProcessContext> action, File workingDirectory) {
+            this.workingDirectory = workingDirectory
+            this.action = action;
+        }
+
+        ChildProcess expectStopFailure() {
+            stopFails = true;
+            return this;
+        }
+
+        ChildProcess expectStartFailure() {
+            startFails = true;
+            return this;
+        }
+
+        public void start() {
+            WorkerProcessBuilder builder = workerFactory.create();
+            builder.worker(action);
+            builder.getJavaCommand().jvmArgs(jvmArgs);
+            builder.getJavaCommand().setWorkingDir(workingDirectory)
+
+            proc = builder.build();
+            try {
+                proc.start();
+                assertFalse(startFails);
+            } catch (ExecException e) {
+                println e
+                e.printStackTrace()
+                assertTrue(startFails);
+                return;
+            }
+            proc.getConnection().addIncoming(TestListenerInterface.class, exceptionListener);
+            if (serverAction != null) {
+                serverAction.execute(proc.getConnection());
+            }
+        }
+
+        public void waitForStop() {
+            if (startFails) {
+                return;
+            }
+            try {
+                proc.waitForStop();
+                assertFalse("Expected process to fail", stopFails);
+            } catch (ExecException e) {
+                assertTrue("Unexpected failure in worker process", stopFails);
+            }
+        }
+
+        public ChildProcess onServer(Action<ObjectConnection> action) {
+            this.serverAction = action;
+            return this;
+        }
+
+        public ChildProcess jvmArgs(String... jvmArgs) {
+            this.jvmArgs = Arrays.asList(jvmArgs);
+            return this;
+        }
+    }
+
+
+    public static class RemoteExceptionListener implements Dispatch<MethodInvocation> {
+        Throwable ex;
+        final Dispatch<MethodInvocation> dispatch;
+
+        public RemoteExceptionListener(Dispatch<MethodInvocation> dispatch) {
+            this.dispatch = dispatch;
+        }
+
+        public void dispatch(MethodInvocation message) {
+            try {
+                dispatch.dispatch(message);
+            } catch (Throwable e) {
+                ex = e;
+            }
+        }
+
+        public void rethrow() throws Throwable {
+            if (ex != null) {
+                throw ex;
+            }
+        }
+    }
+
+    public interface TestListenerInterface {
+        public void send(String message, int count);
+    }
+}
diff --git a/subprojects/core/src/integTest/groovy/org/gradle/process/internal/WorkerProcessIntegrationTest.java b/subprojects/core/src/integTest/groovy/org/gradle/process/internal/WorkerProcessIntegrationTest.java
index ad73aa9..6cb2c63 100644
--- a/subprojects/core/src/integTest/groovy/org/gradle/process/internal/WorkerProcessIntegrationTest.java
+++ b/subprojects/core/src/integTest/groovy/org/gradle/process/internal/WorkerProcessIntegrationTest.java
@@ -29,6 +29,7 @@ import org.gradle.api.internal.file.TestFiles;
 import org.gradle.api.logging.LogLevel;
 import org.gradle.cache.CacheRepository;
 import org.gradle.cache.internal.*;
+import org.gradle.cache.internal.locklistener.NoOpFileLockContentionHandler;
 import org.gradle.internal.id.LongIdGenerator;
 import org.gradle.internal.nativeplatform.ProcessEnvironment;
 import org.gradle.internal.nativeplatform.services.NativeServices;
@@ -71,7 +72,7 @@ public class WorkerProcessIntegrationTest {
     @Rule
     public final TestNameTestDirectoryProvider tmpDir = new TestNameTestDirectoryProvider();
     private final ProcessMetaDataProvider metaDataProvider = new DefaultProcessMetaDataProvider(NativeServices.getInstance().get(ProcessEnvironment.class));
-    private final CacheFactory factory = new DefaultCacheFactory(new DefaultFileLockManager(metaDataProvider)).create();
+    private final CacheFactory factory = new DefaultCacheFactory(new DefaultFileLockManager(metaDataProvider, new NoOpFileLockContentionHandler())).create();
     private final CacheRepository cacheRepository = new DefaultCacheRepository(tmpDir.getTestDirectory(), null, CacheUsage.ON, factory);
     private final ModuleRegistry moduleRegistry = new DefaultModuleRegistry();
     private final ClassPathRegistry classPathRegistry = new DefaultClassPathRegistry(new DefaultClassPathProvider(moduleRegistry), new WorkerProcessClassPathProvider(cacheRepository, moduleRegistry));
diff --git a/subprojects/core/src/main/groovy/org/gradle/BuildListener.java b/subprojects/core/src/main/groovy/org/gradle/BuildListener.java
index a27aaad..3bb5583 100644
--- a/subprojects/core/src/main/groovy/org/gradle/BuildListener.java
+++ b/subprojects/core/src/main/groovy/org/gradle/BuildListener.java
@@ -19,10 +19,8 @@ import org.gradle.api.initialization.Settings;
 import org.gradle.api.invocation.Gradle;
 
 /**
- * <p>A {@code BuildListener} is notified of the major lifecycle events as a {@link GradleLauncher} instance executes a
- * build.</p>
+ * <p>A {@code BuildListener} is notified of the major lifecycle events as a build is executed.</p>
  *
- * @author Hans Dockter
  * @see org.gradle.api.invocation.Gradle#addListener(Object)
  */
 public interface BuildListener {
diff --git a/subprojects/core/src/main/groovy/org/gradle/CacheUsage.java b/subprojects/core/src/main/groovy/org/gradle/CacheUsage.java
index 913c4af..0d044a9 100644
--- a/subprojects/core/src/main/groovy/org/gradle/CacheUsage.java
+++ b/subprojects/core/src/main/groovy/org/gradle/CacheUsage.java
@@ -21,7 +21,6 @@ import org.gradle.api.InvalidUserDataException;
  * <p>{@code CacheUsage} specifies how compiled scripts should be cached.</p>
  *
  * @deprecated This enum has been deprecated. Use {@link StartParameter#isRerunTasks()} and {@link StartParameter#isRecompileScripts()} instead.
- * @author Hans Dockter
  */
 @Deprecated
 public enum CacheUsage {
diff --git a/subprojects/core/src/main/groovy/org/gradle/GradleLauncher.java b/subprojects/core/src/main/groovy/org/gradle/GradleLauncher.java
index 7d58db8..1214dae 100644
--- a/subprojects/core/src/main/groovy/org/gradle/GradleLauncher.java
+++ b/subprojects/core/src/main/groovy/org/gradle/GradleLauncher.java
@@ -15,16 +15,19 @@
  */
 package org.gradle;
 
+import org.gradle.internal.service.scopes.GlobalScopeServices;
 import org.gradle.api.logging.StandardOutputListener;
-import org.gradle.initialization.DefaultGradleLauncherFactory;
 import org.gradle.initialization.GradleLauncherFactory;
+import org.gradle.util.DeprecationLogger;
 
 /**
- * <p>{@code GradleLauncher} is mildly deprecated. It is being replaced by the Tooling API.
- * If you're interested in embedding Gradle you should read the new user guide chapter on embedding Gradle.
+ * <p>Executes a Gradle build.
+ *
+ * <p>{@code GradleLauncher} is deprecated. It has been replaced by the Tooling API.
+ * If you're interested in embedding Gradle you should read the user guide chapter on embedding Gradle.
  * The main entry point to the Tooling API (and embedding Gradle) is {@code org.gradle.tooling.GradleConnector}.
  *
- * <p>You should try using the Tooling API ({@code GradleConnector}) instead of {@code GradleLauncher}.
+ * <p>You should try using the Tooling API (via {@code GradleConnector}) instead of {@code GradleLauncher}.
  * However, if you need some capability that isn't yet implemented in the Tooling API here is how you use {@code GradleLauncher}:
  *
  * <ol>
@@ -46,8 +49,9 @@ import org.gradle.initialization.GradleLauncherFactory;
  *
  * </ol>
  *
- * @author Hans Dockter
+ * @deprecated Use the tooling API instead.
  */
+ at Deprecated
 public abstract class GradleLauncher {
 
     private static GradleLauncherFactory factory;
@@ -82,14 +86,23 @@ public abstract class GradleLauncher {
      *
      * @param startParameter The start parameter object the GradleLauncher instance is initialized with
      * @return The {@code GradleLauncher}. Never returns null.
+     * @deprecated Use the tooling API instead.
      */
-    public static GradleLauncher newInstance(final StartParameter startParameter) {
-        return getFactory().newInstance(startParameter);
+    @Deprecated
+    public static GradleLauncher newInstance(StartParameter startParameter) {
+        DeprecationLogger.nagUserOfDiscontinuedMethod("GradleLauncher.newInstance()");
+        return doGetFactory().newInstance(startParameter);
+    }
+
+    @Deprecated
+    public static GradleLauncherFactory getFactory() {
+        DeprecationLogger.nagUserOfDiscontinuedMethod("GradleLauncher.getFactory()");
+        return doGetFactory();
     }
 
-    public static synchronized GradleLauncherFactory getFactory() {
+    private static synchronized GradleLauncherFactory doGetFactory() {
         if (factory == null) {
-            factory = new DefaultGradleLauncherFactory();
+            factory = new GlobalScopeServices().get(GradleLauncherFactory.class);
         }
         return factory;
     }
@@ -99,9 +112,13 @@ public abstract class GradleLauncher {
      *
      * @param commandLineArgs A String array where each element denotes an entry of the Gradle command line syntax
      * @return The {@code GradleLauncher}. Never returns null.
+     * @deprecated Use the tooling API instead.
      */
+    @Deprecated
     public static GradleLauncher newInstance(String... commandLineArgs) {
-        return newInstance(createStartParameter(commandLineArgs));
+        DeprecationLogger.nagUserOfDiscontinuedMethod("GradleLauncher.newInstance()");
+        GradleLauncherFactory gradleLauncherFactory = doGetFactory();
+        return gradleLauncherFactory.newInstance(gradleLauncherFactory.createStartParameter(commandLineArgs));
     }
 
     /**
@@ -110,11 +127,15 @@ public abstract class GradleLauncher {
      *
      * @param commandLineArgs A String array where each element denotes an entry of the Gradle command line syntax
      * @return The {@code GradleLauncher}. Never returns null.
+     * @deprecated No replacement.
      */
-    public static StartParameter createStartParameter(final String... commandLineArgs) {
-        return getFactory().createStartParameter(commandLineArgs);
+    @Deprecated
+    public static StartParameter createStartParameter(String... commandLineArgs) {
+        DeprecationLogger.nagUserOfDiscontinuedMethod("GradleLauncher.createStartParameter()");
+        return doGetFactory().createStartParameter(commandLineArgs);
     }
 
+    @Deprecated
     public static synchronized void injectCustomFactory(GradleLauncherFactory gradleLauncherFactory) {
         factory = gradleLauncherFactory;
     }
diff --git a/subprojects/core/src/main/groovy/org/gradle/StartParameter.java b/subprojects/core/src/main/groovy/org/gradle/StartParameter.java
index ab9741d..2557618 100644
--- a/subprojects/core/src/main/groovy/org/gradle/StartParameter.java
+++ b/subprojects/core/src/main/groovy/org/gradle/StartParameter.java
@@ -22,11 +22,13 @@ import org.apache.commons.lang.builder.EqualsBuilder;
 import org.apache.commons.lang.builder.HashCodeBuilder;
 import org.gradle.api.Incubating;
 import org.gradle.api.internal.classpath.DefaultModuleRegistry;
+import org.gradle.initialization.BuildLayoutParameters;
 import org.gradle.initialization.CompositeInitScriptFinder;
 import org.gradle.initialization.DistributionInitScriptFinder;
 import org.gradle.initialization.UserHomeInitScriptFinder;
 import org.gradle.internal.SystemProperties;
 import org.gradle.logging.LoggingConfiguration;
+import org.gradle.util.DeprecationLogger;
 import org.gradle.util.GFileUtils;
 
 import java.io.File;
@@ -34,12 +36,13 @@ import java.io.Serializable;
 import java.util.*;
 
 /**
- * <p>{@code StartParameter} defines the configuration used by a {@link GradleLauncher} instance to execute a build. The properties of {@code StartParameter} generally correspond to the command-line
- * options of Gradle. You pass a {@code StartParameter} instance to {@link GradleLauncher#newInstance(StartParameter)} when you create a new {@code Gradle} instance.</p>
+ * <p>{@code StartParameter} defines the configuration used by a Gradle instance to execute a build. The properties of {@code StartParameter} generally
+ * correspond to the command-line options of Gradle.
+ *
+ * <p>You pass a {@code StartParameter} instance to {@link GradleLauncher#newInstance(StartParameter)} when you create a new Gradle instance.</p>
  *
  * <p>You can obtain an instance of a {@code StartParameter} by either creating a new one, or duplicating an existing one using {@link #newInstance} or {@link #newBuild}.</p>
  *
- * @author Hans Dockter
  * @see GradleLauncher
  */
 public class StartParameter extends LoggingConfiguration implements Serializable {
@@ -50,11 +53,11 @@ public class StartParameter extends LoggingConfiguration implements Serializable
     public static final File DEFAULT_GRADLE_USER_HOME = new File(SystemProperties.getUserHome() + "/.gradle");
 
     private List<String> taskNames = new ArrayList<String>();
-    private Set<String> excludedTaskNames = new HashSet<String>();
+    private Set<String> excludedTaskNames = new LinkedHashSet<String>();
     private boolean buildProjectDependencies = true;
     private File currentDir;
     private File projectDir;
-    private boolean searchUpwards = true;
+    private boolean searchUpwards;
     private Map<String, String> projectProperties = new HashMap<String, String>();
     private Map<String, String> systemPropertiesArgs = new HashMap<String, String>();
     private File gradleUserHomeDir;
@@ -74,7 +77,6 @@ public class StartParameter extends LoggingConfiguration implements Serializable
     private boolean recompileScripts;
     private int parallelThreadCount;
     private boolean configureOnDemand;
-    private boolean parallelThreadCountConfigured;
 
     /**
      * Sets the project's cache location. Set to null to use the default location.
@@ -96,18 +98,12 @@ public class StartParameter extends LoggingConfiguration implements Serializable
      * Creates a {@code StartParameter} with default values. This is roughly equivalent to running Gradle on the command-line with no arguments.
      */
     public StartParameter() {
-        String gradleUserHome = System.getProperty(GRADLE_USER_HOME_PROPERTY_KEY);
-        if (gradleUserHome == null) {
-            gradleUserHome = System.getenv("GRADLE_USER_HOME");
-            if (gradleUserHome == null) {
-                gradleUserHome = DEFAULT_GRADLE_USER_HOME.getAbsolutePath();
-            }
-        }
-
-        gradleUserHomeDir = GFileUtils.canonicalise(new File(gradleUserHome));
         gradleHomeDir = new DefaultModuleRegistry().getGradleHome();
 
-        setCurrentDir(null);
+        BuildLayoutParameters layoutDefaults = new BuildLayoutParameters();
+        searchUpwards = layoutDefaults.getSearchUpwards();
+        currentDir = layoutDefaults.getProjectDir();
+        gradleUserHomeDir = layoutDefaults.getGradleUserHomeDir();
     }
 
     /**
@@ -123,7 +119,7 @@ public class StartParameter extends LoggingConfiguration implements Serializable
         p.settingsFile = settingsFile;
         p.useEmptySettings = useEmptySettings;
         p.taskNames = new ArrayList<String>(taskNames);
-        p.excludedTaskNames = new HashSet<String>(excludedTaskNames);
+        p.excludedTaskNames = new LinkedHashSet<String>(excludedTaskNames);
         p.buildProjectDependencies = buildProjectDependencies;
         p.currentDir = currentDir;
         p.searchUpwards = searchUpwards;
@@ -138,7 +134,7 @@ public class StartParameter extends LoggingConfiguration implements Serializable
     }
 
     /**
-     * <p>Creates the parameters for a new build, using these parameters as a template. Copies the environmental properties from this parameter (eg gradle user home dir, etc), but does not copy the
+     * <p>Creates the parameters for a new build, using these parameters as a template. Copies the environmental properties from this parameter (eg Gradle user home dir, etc), but does not copy the
      * build specific properties (eg task names).</p>
      *
      * @return The new parameters.
@@ -285,7 +281,7 @@ public class StartParameter extends LoggingConfiguration implements Serializable
         if (currentDir != null) {
             this.currentDir = GFileUtils.canonicalise(currentDir);
         } else {
-            this.currentDir = GFileUtils.canonicalise(SystemProperties.getCurrentDir());
+            this.currentDir = new BuildLayoutParameters().getProjectDir();
         }
     }
 
@@ -314,11 +310,17 @@ public class StartParameter extends LoggingConfiguration implements Serializable
     }
 
     /**
+     * Deprecated. It is no longer used internally and there's no good reason to keep it.
+     * There is no replacement method.
+     *
      * Returns a newly constructed map that is the JVM system properties merged with the system property args. <p> System property args take precedence over JVM system properties.
      *
      * @return The merged system properties
+     * @deprecated No replacement
      */
+    @Deprecated
     public Map<String, String> getMergedSystemProperties() {
+        DeprecationLogger.nagUserOfDiscontinuedMethod("StartParameter.getMergedSystemProperties()");
         Map<String, String> merged = new HashMap<String, String>();
         merged.putAll((Map) System.getProperties());
         merged.putAll(getSystemPropertiesArgs());
@@ -363,7 +365,7 @@ public class StartParameter extends LoggingConfiguration implements Serializable
     /**
      *  Returns the configured CacheUsage.
      *  @deprecated Use {@link #isRecompileScripts} and/or {@link #isRerunTasks} instead.
-     * */
+      */
     @Deprecated
     public CacheUsage getCacheUsage() {
         return cacheUsage;
@@ -372,7 +374,7 @@ public class StartParameter extends LoggingConfiguration implements Serializable
     /**
      *  Sets the Cache usage.
      *  @deprecated Use {@link #setRecompileScripts} and/or {@link #setRerunTasks} instead.
-     * */
+      */
     @Deprecated
     public void setCacheUsage(CacheUsage cacheUsage) {
         this.cacheUsage = cacheUsage;
@@ -390,7 +392,7 @@ public class StartParameter extends LoggingConfiguration implements Serializable
      * Returns task optimization disabled flag.
      *
      * @deprecated Use {@link #isRerunTasks} instead.
-     * */
+      */
     @Deprecated
     public boolean isNoOpt() {
         return rerunTasks;
@@ -400,7 +402,7 @@ public class StartParameter extends LoggingConfiguration implements Serializable
     * Get task optimization disabled.
     *
     * @param noOpt The boolean value for disabling task optimization.
-    * @deprecated Use {@link #setRefreshDependencies(boolean)} instead.
+    * @deprecated Use {@link #setRerunTasks(boolean)} instead.
     */
     @Deprecated
     public void setNoOpt(boolean noOpt) {
@@ -626,7 +628,6 @@ public class StartParameter extends LoggingConfiguration implements Serializable
      * @see #getParallelThreadCount()
      */
     public void setParallelThreadCount(int parallelThreadCount) {
-        this.parallelThreadCountConfigured = true;
         this.parallelThreadCount = parallelThreadCount;
     }
 
@@ -638,14 +639,6 @@ public class StartParameter extends LoggingConfiguration implements Serializable
         return configureOnDemand;
     }
 
-    @Incubating
-    public boolean isParallelThreadCountConfigured() {
-        //This is not beautiful. As the number of gradle properties grows we may something like:
-        //1. Make StartParameter an interface
-        //2. StartParameter (StartParameterInternal) needs to inform if certain property was configured or not
-        return parallelThreadCountConfigured;
-    }
-
     @Override
     public String toString() {
         return "StartParameter{"
diff --git a/subprojects/core/src/main/groovy/org/gradle/TaskExecutionLogger.java b/subprojects/core/src/main/groovy/org/gradle/TaskExecutionLogger.java
index af71a38..a47d39a 100644
--- a/subprojects/core/src/main/groovy/org/gradle/TaskExecutionLogger.java
+++ b/subprojects/core/src/main/groovy/org/gradle/TaskExecutionLogger.java
@@ -31,7 +31,7 @@ import java.util.Map;
  * A listener which logs the execution of tasks.
  */
 public class TaskExecutionLogger implements TaskExecutionListener {
-    // TODO:PARALLEL Seems to be some thread-safety issues here (get 'failed' logged for wrong task)
+
     private final Map<Task, ProgressLogger> currentTasks = new HashMap<Task, ProgressLogger>();
     private final ProgressLoggerFactory progressLoggerFactory;
 
diff --git a/subprojects/core/src/main/groovy/org/gradle/api/CircularReferenceException.java b/subprojects/core/src/main/groovy/org/gradle/api/CircularReferenceException.java
index 34b7d5c..a7f9244 100644
--- a/subprojects/core/src/main/groovy/org/gradle/api/CircularReferenceException.java
+++ b/subprojects/core/src/main/groovy/org/gradle/api/CircularReferenceException.java
@@ -19,8 +19,6 @@ package org.gradle.api;
 /**
  * <p>A <code>CircularReferenceException</code> is thrown if circular references exists between tasks, the project
  * evaluation order or the project dependsOn order.</p>
- *
- * @author Hans Dockter
  */
 public class CircularReferenceException extends GradleException {
     public CircularReferenceException(String message) {
diff --git a/subprojects/core/src/main/groovy/org/gradle/api/DefaultTask.java b/subprojects/core/src/main/groovy/org/gradle/api/DefaultTask.java
index e34301e..b0a7605 100644
--- a/subprojects/core/src/main/groovy/org/gradle/api/DefaultTask.java
+++ b/subprojects/core/src/main/groovy/org/gradle/api/DefaultTask.java
@@ -23,8 +23,6 @@ import org.gradle.api.internal.NoConventionMapping;
 
 /**
  * {@code DefaultTask} is the standard {@link Task} implementation. You can extend this to implement your own task types.
- *
- * @author Hans Dockter
  */
 @NoConventionMapping
 public class DefaultTask extends AbstractTask {
diff --git a/subprojects/core/src/main/groovy/org/gradle/api/ExtensiblePolymorphicDomainObjectContainer.java b/subprojects/core/src/main/groovy/org/gradle/api/ExtensiblePolymorphicDomainObjectContainer.java
index 5df7fe2..39ebe9c 100644
--- a/subprojects/core/src/main/groovy/org/gradle/api/ExtensiblePolymorphicDomainObjectContainer.java
+++ b/subprojects/core/src/main/groovy/org/gradle/api/ExtensiblePolymorphicDomainObjectContainer.java
@@ -15,6 +15,8 @@
  */
 package org.gradle.api;
 
+import groovy.lang.Closure;
+
 /**
  * A {@link org.gradle.api.PolymorphicDomainObjectContainer} that can be extended at runtime to
  * create elements of new types.
@@ -34,4 +36,30 @@ public interface ExtensiblePolymorphicDomainObjectContainer<T> extends Polymorph
      * @throws IllegalArgumentException if the specified type is not a subtype of the container element type
      */
     public <U extends T> void registerFactory(Class<U> type, NamedDomainObjectFactory<? extends U> factory);
+
+    /**
+     * Registers a factory for creating elements of the specified type.
+     * Typically, the specified type is an interface type.
+     *
+     * @param type the type of objects created by the factory
+     * @param factory the factory to register
+     * @param <U> the type of objects created by the factory
+     *
+     * @throws IllegalArgumentException if the specified type is not a subtype of the container element type
+     */
+    public <U extends T> void registerFactory(Class<U> type, final Closure<? extends U> factory);
+
+    /**
+     * Registers a binding from the specified "public" domain object type to the specified implementation type.
+     * Whenever the container is asked to create an element with the binding's public type, it will instantiate
+     * the binding's implementation type. If the implementation type has a constructor annotated with
+     * {@link javax.inject.Inject}, its arguments will be injected.
+     *
+     * <p>In general, registering a binding is preferable over implementing and registering a factory.
+     *
+     * @param type a public domain object type
+     * @param implementationType the corresponding implementation type
+     * @param <U> a public domain object type
+     */
+    public <U extends T> void registerBinding(Class<U> type, final Class<? extends U> implementationType);
 }
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 544a249..249926d 100644
--- a/subprojects/core/src/main/groovy/org/gradle/api/GradleScriptException.java
+++ b/subprojects/core/src/main/groovy/org/gradle/api/GradleScriptException.java
@@ -20,8 +20,6 @@ import org.gradle.api.internal.Contextual;
 /**
  * <p>A <code>GradleScriptException</code> is thrown when an exception occurs in the compilation or execution of a
  * script.</p>
- *
- * @author Hans Dockter
  */
 @Contextual
 public class GradleScriptException extends GradleException {
diff --git a/subprojects/core/src/main/groovy/org/gradle/api/IllegalDependencyNotation.java b/subprojects/core/src/main/groovy/org/gradle/api/IllegalDependencyNotation.java
index 8d965a8..569babf 100644
--- a/subprojects/core/src/main/groovy/org/gradle/api/IllegalDependencyNotation.java
+++ b/subprojects/core/src/main/groovy/org/gradle/api/IllegalDependencyNotation.java
@@ -17,8 +17,6 @@ package org.gradle.api;
 
 /**
  * This exceptions is thrown, if a dependency is declared with a illegal notation.
- * 
- * @author Hans Dockter
  */
 public class IllegalDependencyNotation extends GradleException {
     public IllegalDependencyNotation() {
diff --git a/subprojects/core/src/main/groovy/org/gradle/api/IllegalOperationAtExecutionTimeException.java b/subprojects/core/src/main/groovy/org/gradle/api/IllegalOperationAtExecutionTimeException.java
index 412c520..8391b3f 100644
--- a/subprojects/core/src/main/groovy/org/gradle/api/IllegalOperationAtExecutionTimeException.java
+++ b/subprojects/core/src/main/groovy/org/gradle/api/IllegalOperationAtExecutionTimeException.java
@@ -19,8 +19,9 @@ package org.gradle.api;
  * A <code>IllegalOperationAtExecutionTimeException</code> is thrown if you try to trigger an operation at runtime,
  * which is only allowed at configuration time.
  *
- * @author Hans Dockter
+ * @deprecated No replacement
  */
+ at Deprecated
 public class IllegalOperationAtExecutionTimeException extends InvalidUserDataException {
     public IllegalOperationAtExecutionTimeException(String message) {
         super(message);
diff --git a/subprojects/core/src/main/groovy/org/gradle/api/InvalidUserDataException.java b/subprojects/core/src/main/groovy/org/gradle/api/InvalidUserDataException.java
index f166d61..0c5865f 100644
--- a/subprojects/core/src/main/groovy/org/gradle/api/InvalidUserDataException.java
+++ b/subprojects/core/src/main/groovy/org/gradle/api/InvalidUserDataException.java
@@ -18,8 +18,6 @@ package org.gradle.api;
 
 /**
  * A <code>InvalidUserDataException</code> is thrown, if a user is providing illegal data for the build.
- *
- * @author Hans Dockter
  */
 public class InvalidUserDataException extends GradleException {
     public InvalidUserDataException() {
diff --git a/subprojects/core/src/main/groovy/org/gradle/api/NamedDomainObjectCollection.java b/subprojects/core/src/main/groovy/org/gradle/api/NamedDomainObjectCollection.java
index aabc1a6..d5d5e82 100644
--- a/subprojects/core/src/main/groovy/org/gradle/api/NamedDomainObjectCollection.java
+++ b/subprojects/core/src/main/groovy/org/gradle/api/NamedDomainObjectCollection.java
@@ -21,6 +21,7 @@ import org.gradle.api.specs.Spec;
 import java.util.Collection;
 import java.util.List;
 import java.util.SortedMap;
+import java.util.SortedSet;
 
 /**
  * <p>A {@code NamedDomainObjectCollection} represents a collection of domain objects that have an inherent, constant, name.</p>
@@ -102,6 +103,15 @@ public interface NamedDomainObjectCollection<T> extends DomainObjectCollection<T
     SortedMap<String, T> getAsMap();
 
     /**
+     * <p>Returns the names of the objects in this collection as a Set of Strings.</p>
+     *
+     * <p>The set of names is in <em>natural ordering</em>.</p>
+     *
+     * @return The names. Returns an empty set if this collection is empty.
+     */
+    SortedSet<String> getNames();
+
+    /**
      * Locates an object by name, returning null if there is no such object.
      *
      * @param name The object name
@@ -120,7 +130,7 @@ public interface NamedDomainObjectCollection<T> extends DomainObjectCollection<T
 
     /**
      * Locates an object by name, failing if there is no such object. The given configure closure is executed against
-     * the object before it is returned from this method. The object is passed to the closure as it's delegate.
+     * the object before it is returned from this method. The object is passed to the closure as its delegate.
      *
      * @param name The object name
      * @param configureClosure The closure to use to configure the object.
@@ -179,4 +189,4 @@ public interface NamedDomainObjectCollection<T> extends DomainObjectCollection<T
      */
     NamedDomainObjectCollection<T> matching(Closure spec);
 
-}
\ No newline at end of file
+}
diff --git a/subprojects/core/src/main/groovy/org/gradle/api/NamedDomainObjectContainer.java b/subprojects/core/src/main/groovy/org/gradle/api/NamedDomainObjectContainer.java
index fa836d8..8532bfb 100644
--- a/subprojects/core/src/main/groovy/org/gradle/api/NamedDomainObjectContainer.java
+++ b/subprojects/core/src/main/groovy/org/gradle/api/NamedDomainObjectContainer.java
@@ -61,6 +61,16 @@ public interface NamedDomainObjectContainer<T> extends NamedDomainObjectSet<T>,
     T create(String name, Closure configureClosure) throws InvalidUserDataException;
 
     /**
+     * Creates a new item with the given name, adding it to this container, then configuring it with the given action.
+     *
+     * @param name The name to assign to the created object
+     * @param configureAction The action to configure the created object with
+     * @return The created object. Never null.
+     * @throws InvalidUserDataException if an object with the given name already exists in this container.
+     */
+    T create(String name, Action<? super T> configureAction) throws InvalidUserDataException;
+
+    /**
      * <p>Allows the container to be configured, creating missing objects as they are referenced.</p>
      * 
      * <p>TODO: example usage</p>
diff --git a/subprojects/core/src/main/groovy/org/gradle/api/NonExtensible.java b/subprojects/core/src/main/groovy/org/gradle/api/NonExtensible.java
new file mode 100644
index 0000000..d927381
--- /dev/null
+++ b/subprojects/core/src/main/groovy/org/gradle/api/NonExtensible.java
@@ -0,0 +1,31 @@
+/*
+ * Copyright 2013 the original author or authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF 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 java.lang.annotation.*;
+
+/**
+ * Indicates that the type, when DSL enhanced, does not support extensibility.
+ * <p>
+ * This means that it will not be enhanced with {@link org.gradle.api.plugins.ExtensionAware}.
+ */
+ at Incubating
+ at Documented
+ at Retention(RetentionPolicy.RUNTIME)
+ at Inherited
+ at Target({ElementType.TYPE})
+public @interface NonExtensible {}
diff --git a/subprojects/core/src/main/groovy/org/gradle/api/Plugin.java b/subprojects/core/src/main/groovy/org/gradle/api/Plugin.java
index 67e207f..6ed1d38 100644
--- a/subprojects/core/src/main/groovy/org/gradle/api/Plugin.java
+++ b/subprojects/core/src/main/groovy/org/gradle/api/Plugin.java
@@ -20,8 +20,7 @@ package org.gradle.api;
  * Usually, this target object is a {@link org.gradle.api.Project}, but plugins can be applied to any type of
  * objects.</p>
  *
- * @author Hans Dockter
- * @param <T> The type of object which this plugin can configure. 
+ * @param <T> The type of object which this plugin can configure.
  */
 public interface Plugin<T> {
     /**
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 8d97a1e..a9d998c 100644
--- a/subprojects/core/src/main/groovy/org/gradle/api/Project.java
+++ b/subprojects/core/src/main/groovy/org/gradle/api/Project.java
@@ -32,10 +32,7 @@ import org.gradle.api.internal.HasInternalProtocol;
 import org.gradle.api.invocation.Gradle;
 import org.gradle.api.logging.Logger;
 import org.gradle.api.logging.LoggingManager;
-import org.gradle.api.plugins.Convention;
-import org.gradle.api.plugins.ExtensionAware;
-import org.gradle.api.plugins.ExtensionContainer;
-import org.gradle.api.plugins.PluginContainer;
+import org.gradle.api.plugins.*;
 import org.gradle.api.resources.ResourceHandler;
 import org.gradle.api.tasks.TaskContainer;
 import org.gradle.api.tasks.WorkResult;
@@ -190,11 +187,9 @@ import java.util.Set;
  * <li>The parent project, recursively up to the root project.</li>
  *
  * </ul>
- *
- * @author Hans Dockter
  */
 @HasInternalProtocol
-public interface Project extends Comparable<Project>, ExtensionAware {
+public interface Project extends Comparable<Project>, ExtensionAware, PluginAware {
     /**
      * The default project build file name.
      */
@@ -973,7 +968,7 @@ public interface Project extends Comparable<Project>, ExtensionAware {
      * </target>
      * </pre>
      *
-     * Here's how it would look like in gradle. Observe how the ant xml is represented in groovy by the ant builder
+     * Here's how it would look like in gradle. Observe how the ant XML is represented in groovy by the ant builder
      * <pre autoTested=''>
      * task printChecksum {
      *   doLast {
diff --git a/subprojects/core/src/main/groovy/org/gradle/api/ProjectConfigurationException.java b/subprojects/core/src/main/groovy/org/gradle/api/ProjectConfigurationException.java
new file mode 100644
index 0000000..ffbd0ec
--- /dev/null
+++ b/subprojects/core/src/main/groovy/org/gradle/api/ProjectConfigurationException.java
@@ -0,0 +1,29 @@
+/*
+ * Copyright 2013 the original author or authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF 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.internal.Contextual;
+
+/**
+ * Indicates a problem that occurs during project configuration (evaluation) phase.
+ */
+ at Contextual
+public class ProjectConfigurationException extends GradleException {
+    public ProjectConfigurationException(String message, Throwable cause) {
+        super(message, cause);
+    }
+}
diff --git a/subprojects/core/src/main/groovy/org/gradle/api/ProjectEvaluationListener.java b/subprojects/core/src/main/groovy/org/gradle/api/ProjectEvaluationListener.java
index f91cdff..f5e9f74 100644
--- a/subprojects/core/src/main/groovy/org/gradle/api/ProjectEvaluationListener.java
+++ b/subprojects/core/src/main/groovy/org/gradle/api/ProjectEvaluationListener.java
@@ -19,8 +19,6 @@ package org.gradle.api;
  * <p>An {@code ProjectEvaluationListener} is notified when a project is evaluated. You add can add an {@code
  * ProjectEvaluationListener} to a {@link org.gradle.api.invocation.Gradle} using {@link
  * org.gradle.api.invocation.Gradle#addProjectEvaluationListener(ProjectEvaluationListener)}.</p>
- *
- * @author Hans Dockter
  */
 public interface ProjectEvaluationListener {
     /**
diff --git a/subprojects/core/src/main/groovy/org/gradle/api/ProjectState.java b/subprojects/core/src/main/groovy/org/gradle/api/ProjectState.java
index 5fb4a1d..230552f 100644
--- a/subprojects/core/src/main/groovy/org/gradle/api/ProjectState.java
+++ b/subprojects/core/src/main/groovy/org/gradle/api/ProjectState.java
@@ -16,9 +16,12 @@
 
 package org.gradle.api;
 
+import org.gradle.api.internal.HasInternalProtocol;
+
 /**
  * {@code ProjectState} provides information about the execution state of a project.
  */
+ at HasInternalProtocol
 public interface ProjectState {
     /**
      * <p>Returns true if this project has been evaluated.</p>
diff --git a/subprojects/core/src/main/groovy/org/gradle/api/Rule.java b/subprojects/core/src/main/groovy/org/gradle/api/Rule.java
index b589e76..34d6e91 100644
--- a/subprojects/core/src/main/groovy/org/gradle/api/Rule.java
+++ b/subprojects/core/src/main/groovy/org/gradle/api/Rule.java
@@ -18,8 +18,6 @@ package org.gradle.api;
 /**
  * <p>A {@code Rule} represents some action to perform when an unknown domain object is referenced. The rule can use the
  * domain object name to add an implicit domain object.</p>
- *
- * @author Hans Dockter
  */
 public interface Rule {
     /**
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 6d24910..47c271c 100644
--- a/subprojects/core/src/main/groovy/org/gradle/api/Task.java
+++ b/subprojects/core/src/main/groovy/org/gradle/api/Task.java
@@ -69,12 +69,15 @@ import java.util.Set;
  * next task by throwing a {@link org.gradle.api.tasks.StopExecutionException}. Using these exceptions allows you to
  * have precondition actions which skip execution of the task, or part of the task, if not true.</p>
  *
- * <a name="dependencies"/><h3>Dependencies</h3>
+ * <a name="dependencies"/><h3>Task Dependencies and Task Ordering</h3>
  *
- * <p>A task may have dependencies on other tasks. Gradle ensures that tasks are executed in dependency order, so that
- * the dependencies of a task are executed before the task is executed.  You can add dependencies to a task using {@link
- * #dependsOn(Object...)} or {@link #setDependsOn(Iterable)}.  You can add objects of any of the following types as a
- * dependency:</p>
+ * <p>A task may have dependencies on other tasks or might be scheduled to always run after another task.
+ * Gradle ensures that all task dependencies and ordering rules are honored when executing tasks, so that the task is executed after
+ * all of its dependencies and any "must run after" tasks have been executed.</p>
+ *
+ * <p>Dependencies to a task are controlled using {@link #dependsOn(Object...)} or {@link #setDependsOn(Iterable)},
+ * and {@link #mustRunAfter(Object...)} or {@link #setMustRunAfter(Iterable)} are used to specify ordering between tasks. You can use objects
+ * of any of the following types to specify dependencies and ordering:</p>
  *
  * <ul>
  *
@@ -127,8 +130,6 @@ import java.util.Set;
  * <h4>Dynamic Methods</h4>
  *
  * <p>A {@link Plugin} may add methods to a {@code Task} using its {@link Convention} object.</p>
- *
- * @author Hans Dockter
  */
 public interface Task extends Comparable<Task>, ExtensionAware {
     public static final String TASK_NAME = "name";
@@ -538,5 +539,99 @@ public interface Task extends Comparable<Task>, ExtensionAware {
      * @return The directory. Never returns null. The directory will already exist.
      */
     File getTemporaryDir();
+
+    /**
+     * <p>Specifies that this task must run after all of the supplied tasks.</p>
+     *
+     * <pre autoTested="true">
+     * task taskY {
+     *     mustRunAfter "taskX"
+     * }
+     * </pre>
+     *
+     * <p>For each supplied task, this action adds a task 'ordering', and does not specify a 'dependency' between the tasks.
+     * As such, it is still possible to execute 'taskY' without first executing the 'taskX' in the example.</p>
+     *
+     * <p>See <a href="#dependencies">here</a> for a description of the types of objects which can be used to specify
+     * an ordering relationship.</p>
+     *
+     * @param paths The tasks this task must run after.
+     *
+     * @return the task object this method is applied to
+     */
+    @Incubating
+    Task mustRunAfter(Object... paths);
+
+    /**
+     * <p>Specifies the set of tasks that this task must run after.</p>
+     *
+     * <pre autoTested="true">
+     * task taskY {
+     *     mustRunAfter = ["taskX1", "taskX2"]
+     * }
+     * </pre>
+     *
+     * <p>For each supplied task, this action adds a task 'ordering', and does not specify a 'dependency' between the tasks.
+     * As such, it is still possible to execute 'taskY' without first executing the 'taskX' in the example.</p>
+     *
+     * <p>See <a href="#dependencies">here</a> for a description of the types of objects which can be used to specify
+     * an ordering relationship.</p>
+     *
+     * @param mustRunAfter The set of task paths this task must run after.
+     */
+    @Incubating
+    void setMustRunAfter(Iterable<?> mustRunAfter);
+
+    /**
+     * <p>Returns tasks that this task must run after.</p>
+     *
+     * @return The tasks that this task must run after. Returns an empty set if this task has no tasks it must run after.
+     */
+    @Incubating
+    TaskDependency getMustRunAfter();
+
+    /**
+     * <p>Adds the given finalizer tasks for this task.</p>
+     *
+     * <pre autoTested="true">
+     * task taskY {
+     *     finalizedBy "taskX"
+     * }
+     * </pre>
+     *
+     * <p>See <a href="#dependencies">here</a> for a description of the types of objects which can be used to specify
+     * a finalizer task.</p>
+     *
+     * @param paths The tasks that finalize this task.
+     *
+     * @return the task object this method is applied to
+     */
+    @Incubating
+    Task finalizedBy(Object... paths);
+
+    /**
+     * <p>Specifies the set of finalizer tasks for this task.</p>
+     *
+     * <pre autoTested="true">
+     * task taskY {
+     *     finalizedBy = ["taskX1", "taskX2"]
+     * }
+     * </pre>
+     *
+     * <p>See <a href="#dependencies">here</a> for a description of the types of objects which can be used to specify
+     * a finalizer task.</p>
+     *
+     * @param finalizedBy The tasks that finalize this task.
+     */
+    @Incubating
+    void setFinalizedBy(Iterable<?> finalizedBy);
+
+    /**
+     * <p>Returns tasks that finalize this task.</p>
+     *
+     * @return The tasks that finalize this task. Returns an empty set if there are no finalising tasks for this task.
+     */
+    @Incubating
+    TaskDependency getFinalizedBy();
 }
 
diff --git a/subprojects/core/src/main/groovy/org/gradle/api/UnknownProjectException.java b/subprojects/core/src/main/groovy/org/gradle/api/UnknownProjectException.java
index 1653dbb..519213a 100644
--- a/subprojects/core/src/main/groovy/org/gradle/api/UnknownProjectException.java
+++ b/subprojects/core/src/main/groovy/org/gradle/api/UnknownProjectException.java
@@ -18,8 +18,6 @@ package org.gradle.api;
 
 /**
  * <p>An <code>UnknownProjectException</code> is thrown when a project referenced by path cannot be found.</p>
- *
- * @author Hans Dockter
  */
 public class UnknownProjectException extends UnknownDomainObjectException {
     public UnknownProjectException(String message) {
diff --git a/subprojects/core/src/main/groovy/org/gradle/api/UnknownTaskException.java b/subprojects/core/src/main/groovy/org/gradle/api/UnknownTaskException.java
index 70aa432..2a87031 100644
--- a/subprojects/core/src/main/groovy/org/gradle/api/UnknownTaskException.java
+++ b/subprojects/core/src/main/groovy/org/gradle/api/UnknownTaskException.java
@@ -18,8 +18,6 @@ package org.gradle.api;
 
 /**
  * <p>An <code>UnknownTaskException</code> is thrown when a task referenced by path cannot be found.</p>
- *
- * @author Hans Dockter
  */
 public class UnknownTaskException extends UnknownDomainObjectException {
     public UnknownTaskException(String message) {
diff --git a/subprojects/core/src/main/groovy/org/gradle/api/XmlProvider.java b/subprojects/core/src/main/groovy/org/gradle/api/XmlProvider.java
index 7f2e961..4958ab0 100644
--- a/subprojects/core/src/main/groovy/org/gradle/api/XmlProvider.java
+++ b/subprojects/core/src/main/groovy/org/gradle/api/XmlProvider.java
@@ -20,8 +20,6 @@ import org.w3c.dom.Element;
 
 /**
  * Provides various ways to access the content of an XML document.
- *
- * @author Hans Dockter
  */
 public interface XmlProvider {
     /**
diff --git a/subprojects/core/src/main/groovy/org/gradle/api/artifacts/ArtifactIdentifier.java b/subprojects/core/src/main/groovy/org/gradle/api/artifacts/ArtifactIdentifier.java
index 85cc620..cd02402 100644
--- a/subprojects/core/src/main/groovy/org/gradle/api/artifacts/ArtifactIdentifier.java
+++ b/subprojects/core/src/main/groovy/org/gradle/api/artifacts/ArtifactIdentifier.java
@@ -25,13 +25,13 @@ public interface ArtifactIdentifier {
     ModuleVersionIdentifier getModuleVersionIdentifier();
 
     /**
-     * Returns the name of the dependency artifact.
+     * Returns the name of this artifact.
      */
     String getName();
 
     /**
-     * Returns the type of the dependency artifact. Often the type is the same as the extension,
-     * but sometimes this is not the case. For example for an ivy xml module descriptor, the type is
+     * Returns the type of this artifact. Often the type is the same as the extension,
+     * but sometimes this is not the case. For example for an ivy XML module descriptor, the type is
      * <em>ivy</em> and the extension is <em>xml</em>.
      *
      * @see #getExtension()
@@ -39,8 +39,8 @@ public interface ArtifactIdentifier {
     String getType();
 
     /**
-     * Returns the extension of this dependency artifact. Often the extension is the same as the type,
-     * but sometimes this is not the case. For example for an ivy xml module descriptor, the type is
+     * Returns the extension of this artifact. Often the extension is the same as the type,
+     * but sometimes this is not the case. For example for an ivy XML module descriptor, the type is
      * <em>ivy</em> and the extension is <em>xml</em>.
      *
      * @see #getType()
@@ -48,7 +48,7 @@ public interface ArtifactIdentifier {
     String getExtension();
 
     /**
-     * Returns the classifier of this dependency artifact.
+     * Returns the classifier of this artifact, if any.
      */
     String getClassifier();
 
diff --git a/subprojects/core/src/main/groovy/org/gradle/api/artifacts/ArtifactRepositoryContainer.java b/subprojects/core/src/main/groovy/org/gradle/api/artifacts/ArtifactRepositoryContainer.java
index 14cbc80..9640e79 100644
--- a/subprojects/core/src/main/groovy/org/gradle/api/artifacts/ArtifactRepositoryContainer.java
+++ b/subprojects/core/src/main/groovy/org/gradle/api/artifacts/ArtifactRepositoryContainer.java
@@ -33,33 +33,37 @@ import java.util.List;
  * <p>The resolvers in a container are accessible as read-only properties of the container, using the name of the
  * resolver as the property name. For example:</p>
  *
- * <pre>
- * resolvers.addLast(name: 'myResolver')
- * resolvers.myResolver.url = 'some-url'
+ * <pre autoTested=''>
+ * repositories.maven { name 'myResolver' }
+ * repositories.myResolver.url = 'some-url'
  * </pre>
  *
  * <p>A dynamic method is added for each resolver which takes a configuration closure. This is equivalent to calling
  * {@link #getByName(String, groovy.lang.Closure)}. For example:</p>
  *
- * <pre>
- * resolvers.addLast(name: 'myResolver')
- * resolvers.myResolver {
+ * <pre autoTested=''>
+ * repositories.maven { name 'myResolver' }
+ * repositories.myResolver {
  *     url 'some-url'
  * }
  * </pre>
- *
- * @author Hans Dockter
  */
 public interface ArtifactRepositoryContainer extends NamedDomainObjectList<ArtifactRepository>, Configurable<ArtifactRepositoryContainer> {
     String DEFAULT_MAVEN_CENTRAL_REPO_NAME = "MavenRepo";
     String DEFAULT_MAVEN_LOCAL_REPO_NAME = "MavenLocal";
     String MAVEN_CENTRAL_URL = "http://repo1.maven.org/maven2/";
+    @Deprecated
     String MAVEN_REPO_PATTERN = "[organisation]/[module]/[revision]/[artifact]-[revision](-[classifier]).[ext]";
+    @Deprecated
     String DEFAULT_CACHE_ARTIFACT_PATTERN
             = "[organisation]/[module](/[branch])/[type]s/[artifact]-[revision](-[classifier])(.[ext])";
+    @Deprecated
     String DEFAULT_CACHE_IVY_PATTERN = "[organisation]/[module](/[branch])/ivy-[revision].xml";
+    @Deprecated
     String INTERNAL_REPOSITORY_NAME = "internal-repository";
+    @Deprecated
     String RESOLVER_NAME = "name";
+    @Deprecated
     String RESOLVER_URL = "url";
 
     /**
@@ -87,7 +91,9 @@ public interface ArtifactRepositoryContainer extends NamedDomainObjectList<Artif
      * Adds a repository to this container, at the end of the repository sequence.
      *
      * @param resolver The repository to add, represented as an Ivy {@link DependencyResolver}.
+     * @deprecated Use one of the repository methods on {@link org.gradle.api.artifacts.dsl.RepositoryHandler} or {@link #add(ArtifactRepository)} instead.
      */
+    @Deprecated
     boolean add(DependencyResolver resolver);
 
     /**
@@ -95,7 +101,9 @@ public interface ArtifactRepositoryContainer extends NamedDomainObjectList<Artif
      *
      * @param resolver The repository to add, represented as an Ivy {@link DependencyResolver}.
      * @param configureClosure The closure to use to configure the repository.
+     * @deprecated Use one of the repository methods on {@link org.gradle.api.artifacts.dsl.RepositoryHandler} or {@link #add(ArtifactRepository)} instead.
      */
+    @Deprecated
     boolean add(DependencyResolver resolver, Closure configureClosure);
 
     /**
@@ -104,12 +112,12 @@ public interface ArtifactRepositoryContainer extends NamedDomainObjectList<Artif
      *
      * <ul>
      *
-     * <li>A String. This is treated as a URL, and used to create a maven repository.</li>
+     * <li>A String. This is treated as a URL, and used to create a Maven repository.</li>
      *
-     * <li>A map. This is used to create a maven maven repository. The map must contain an {@value #RESOLVER_NAME} entry and a
+     * <li>A map. This is used to create a Maven repository. The map must contain an {@value #RESOLVER_NAME} entry and a
      * {@value #RESOLVER_URL} entry.</li>
      *
-     * <li>A {@link org.apache.ivy.plugins.resolver.DependencyResolver}.</li>
+     * <li>A {@link DependencyResolver}.</li>
      *
      * <li>A {@link ArtifactRepository}.</li>
      *
@@ -144,7 +152,9 @@ public interface ArtifactRepositoryContainer extends NamedDomainObjectList<Artif
      * @return The added resolver.
      * @throws InvalidUserDataException when a resolver with the given name already exists in this container.
      * @throws UnknownRepositoryException when the given next resolver does not exist in this container.
+     * @deprecated No replacement
      */
+    @Deprecated
     DependencyResolver addBefore(Object userDescription, String nextResolver) throws InvalidUserDataException;
 
     /**
@@ -157,7 +167,9 @@ public interface ArtifactRepositoryContainer extends NamedDomainObjectList<Artif
      * @return The added resolver.
      * @throws InvalidUserDataException when a resolver with the given name already exists in this container.
      * @throws UnknownRepositoryException when the given next resolver does not exist in this container.
+     * @deprecated No replacement
      */
+    @Deprecated
     DependencyResolver addBefore(Object userDescription, String nextResolver, Closure configureClosure)
             throws InvalidUserDataException;
 
@@ -169,7 +181,9 @@ public interface ArtifactRepositoryContainer extends NamedDomainObjectList<Artif
      * @return The added resolver.
      * @throws InvalidUserDataException when a resolver with the given name already exists in this container.
      * @throws UnknownRepositoryException when the given previous resolver does not exist in this container.
+     * @deprecated No replacement
      */
+    @Deprecated
     DependencyResolver addAfter(Object userDescription, String previousResolver) throws InvalidUserDataException;
 
     /**
@@ -182,7 +196,9 @@ public interface ArtifactRepositoryContainer extends NamedDomainObjectList<Artif
      * @return The added resolver.
      * @throws InvalidUserDataException when a resolver with the given name already exists in this container.
      * @throws UnknownRepositoryException when the given previous resolver does not exist in this container.
+     * @deprecated No replacement
      */
+    @Deprecated
     DependencyResolver addAfter(Object userDescription, String previousResolver, Closure configureClosure)
             throws InvalidUserDataException;
 
@@ -192,7 +208,9 @@ public interface ArtifactRepositoryContainer extends NamedDomainObjectList<Artif
      * @param userDescription The resolver definition. See {@link #addLast(Object)} for details of this parameter.
      * @return The added resolver.
      * @throws InvalidUserDataException when a resolver with the given name already exists in this container.
+     * @deprecated Use {@link #addFirst(ArtifactRepository)} instead.
      */
+    @Deprecated
     DependencyResolver addFirst(Object userDescription) throws InvalidUserDataException;
 
     /**
@@ -203,7 +221,9 @@ public interface ArtifactRepositoryContainer extends NamedDomainObjectList<Artif
      * @param configureClosure The closure to use to configure the resolver.
      * @return The added resolver.
      * @throws InvalidUserDataException when a resolver with the given name already exists in this container.
+     * @deprecated Use {@link #addFirst(ArtifactRepository)} instead.
      */
+    @Deprecated
     DependencyResolver addFirst(Object userDescription, Closure configureClosure) throws InvalidUserDataException;
 
     /**
diff --git a/subprojects/core/src/main/groovy/org/gradle/api/artifacts/ClientModule.java b/subprojects/core/src/main/groovy/org/gradle/api/artifacts/ClientModule.java
index e4869e6..198c13d 100644
--- a/subprojects/core/src/main/groovy/org/gradle/api/artifacts/ClientModule.java
+++ b/subprojects/core/src/main/groovy/org/gradle/api/artifacts/ClientModule.java
@@ -22,8 +22,6 @@ import java.util.Set;
  * artifact or you declare a module dependency that depends on a module descriptor in a repository. With
  * a client module you can declare a module dependency without the need of a module descriptor in a
  * remote repository.
- *
- * @author Hans Dockter
  */
 public interface ClientModule extends ExternalDependency {
     /**
diff --git a/subprojects/core/src/main/groovy/org/gradle/api/artifacts/ComponentMetadataDetails.java b/subprojects/core/src/main/groovy/org/gradle/api/artifacts/ComponentMetadataDetails.java
new file mode 100644
index 0000000..2dd34c3
--- /dev/null
+++ b/subprojects/core/src/main/groovy/org/gradle/api/artifacts/ComponentMetadataDetails.java
@@ -0,0 +1,86 @@
+/*
+ * Copyright 2013 the original author or authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF 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.Incubating;
+import org.gradle.api.NonExtensible;
+
+import java.util.List;
+
+/**
+ * Describes a resolved component's metadata, which typically originates from
+ * a component descriptor (Ivy file, Maven POM). Some parts of the metadata can be changed
+ * via metadata rules (see {@link org.gradle.api.artifacts.dsl.ComponentMetadataHandler}.
+ *
+ * @since 1.8
+ */
+ at Incubating
+ at NonExtensible
+public interface ComponentMetadataDetails {
+    /**
+     * Returns the identifier of the component.
+     *
+     * @return the identifier of the component.
+     */
+    ModuleVersionIdentifier getId();
+
+    /**
+     * Tells whether the component is changing or immutable.
+     *
+     * @return whether the component is changing or immutable.
+     */
+    boolean isChanging();
+
+    /**
+     * Returns the status of the component. Must
+     * match one of the values in {@link #getStatusScheme()}.
+     *
+     * @return the status of the component
+     */
+    String getStatus();
+
+    /**
+     * Returns the status scheme of the component. Values are
+     * ordered from least to most mature status.
+     * Defaults to {@code ["integration", "milestone", "release"]}.
+     *
+     * @return the status scheme of the component
+     */
+    List<String> getStatusScheme();
+
+    /**
+     * Sets whether the component is changing or immutable.
+     *
+     * @param changing whether the component is changing or immutable
+     */
+    void setChanging(boolean changing);
+
+    /**
+     * Sets the status of the component. Must
+     * match one of the values in {@link #getStatusScheme()}.
+     *
+     * @param status the status of the component
+     */
+    void setStatus(String status);
+
+    /**
+     * Sets the status scheme of the component. Values are ordered
+     * from least to most mature status.
+     *
+     * @param statusScheme the status scheme of the component
+     */
+    void setStatusScheme(List<String> statusScheme);
+}
diff --git a/subprojects/core/src/main/groovy/org/gradle/api/artifacts/ConfigurationContainer.java b/subprojects/core/src/main/groovy/org/gradle/api/artifacts/ConfigurationContainer.java
index db1890f..8257eac 100644
--- a/subprojects/core/src/main/groovy/org/gradle/api/artifacts/ConfigurationContainer.java
+++ b/subprojects/core/src/main/groovy/org/gradle/api/artifacts/ConfigurationContainer.java
@@ -29,16 +29,16 @@ import org.gradle.api.internal.HasInternalProtocol;
  * <p>The configurations in a container are accessible as read-only properties of the container, using the name of the
  * configuration as the property name. For example:</p>
  *
- * <pre>
- * configurations.add('myConfiguration')
+ * <pre autoTested='true'>
+ * configurations.create('myConfiguration')
  * configurations.myConfiguration.transitive = false
  * </pre>
  *
  * <p>A dynamic method is added for each configuration which takes a configuration closure. This is equivalent to
  * calling {@link #getByName(String, groovy.lang.Closure)}. For example:</p>
  *
- * <pre>
- * configurations.add('myConfiguration')
+ * <pre autoTested='true'>
+ * configurations.create('myConfiguration')
  * configurations.myConfiguration {
  *     transitive = false
  * }
@@ -79,8 +79,6 @@ import org.gradle.api.internal.HasInternalProtocol;
  * </pre>
  *
  * Examples on configuring the <b>resolution strategy</b> - see docs for {@link ResolutionStrategy}
- *
- * @author Hans Dockter
  */
 @HasInternalProtocol
 public interface ConfigurationContainer extends NamedDomainObjectContainer<Configuration> {
@@ -105,7 +103,9 @@ public interface ConfigurationContainer extends NamedDomainObjectContainer<Confi
      * @param name The name of the new configuration.
      * @return The newly added configuration.
      * @throws InvalidUserDataException when a configuration with the given name already exists in this container.
+     * @deprecated use {@link #create(String)} instead
      */
+    @Deprecated
     Configuration add(String name) throws InvalidUserDataException;
 
     /**
@@ -116,7 +116,9 @@ public interface ConfigurationContainer extends NamedDomainObjectContainer<Confi
      * @param configureClosure The closure to use to configure the configuration.
      * @return The newly added configuration.
      * @throws InvalidUserDataException when a configuration with the given name already exists in this container.
+     * @deprecated use {@link #create(String, groovy.lang.Closure)} instead
      */
+    @Deprecated
     Configuration add(String name, Closure configureClosure) throws InvalidUserDataException;
 
     /**
diff --git a/subprojects/core/src/main/groovy/org/gradle/api/artifacts/Dependency.java b/subprojects/core/src/main/groovy/org/gradle/api/artifacts/Dependency.java
index bdcf798..267a13c 100644
--- a/subprojects/core/src/main/groovy/org/gradle/api/artifacts/Dependency.java
+++ b/subprojects/core/src/main/groovy/org/gradle/api/artifacts/Dependency.java
@@ -17,14 +17,11 @@ package org.gradle.api.artifacts;
 
 /**
  * A {@code Dependency} represents a dependency on the artifacts from a particular source. A source can be an Ivy
- * module, a Maven pom, another Gradle project, a collection of Files, etc... A source can have zero or more artifacts.
- *
- * @author Hans Dockter
+ * module, a Maven POM, another Gradle project, a collection of Files, etc... A source can have zero or more artifacts.
  */
 public interface Dependency {
     String DEFAULT_CONFIGURATION = "default";
     String ARCHIVES_CONFIGURATION = "archives";
-    // todo Remove to ivy layer in 1.0
     String CLASSIFIER = "m:classifier";
 
     /**
diff --git a/subprojects/core/src/main/groovy/org/gradle/api/artifacts/DependencyArtifact.java b/subprojects/core/src/main/groovy/org/gradle/api/artifacts/DependencyArtifact.java
index 9e8c9c9..4c89482 100644
--- a/subprojects/core/src/main/groovy/org/gradle/api/artifacts/DependencyArtifact.java
+++ b/subprojects/core/src/main/groovy/org/gradle/api/artifacts/DependencyArtifact.java
@@ -18,8 +18,6 @@ package org.gradle.api.artifacts;
 /**
  * <p>An {@code Artifact} represents an artifact included in a {@link org.gradle.api.artifacts.Dependency}.</p>
  * An artifact is an (immutable) value object.
- *
- * @author Hans Dockter
  */
 public interface DependencyArtifact {
     String DEFAULT_TYPE = "jar";
@@ -31,7 +29,7 @@ public interface DependencyArtifact {
 
     /**
      * Returns the type of the dependency artifact. Often the type is the same as the extension,
-     * but sometimes this is not the case. For example for an ivy xml module descriptor, the type is
+     * but sometimes this is not the case. For example for an ivy XML module descriptor, the type is
      * <em>ivy</em> and the extension is <em>xml</em>.
      *
      * @see #getExtension() 
@@ -40,7 +38,7 @@ public interface DependencyArtifact {
 
     /**
      * Returns the extension of this dependency artifact. Often the extension is the same as the type,
-     * but sometimes this is not the case. For example for an ivy xml module descriptor, the type is
+     * but sometimes this is not the case. For example for an ivy XML module descriptor, the type is
      * <em>ivy</em> and the extension is <em>xml</em>.
      *
      * @see #getType() 
diff --git a/subprojects/core/src/main/groovy/org/gradle/api/artifacts/ExcludeRule.java b/subprojects/core/src/main/groovy/org/gradle/api/artifacts/ExcludeRule.java
index 9262a7a..bbd4f38 100644
--- a/subprojects/core/src/main/groovy/org/gradle/api/artifacts/ExcludeRule.java
+++ b/subprojects/core/src/main/groovy/org/gradle/api/artifacts/ExcludeRule.java
@@ -20,8 +20,6 @@ import java.util.Map;
 /**
  * An {@code ExcludeRule} is used to describe transitive dependencies that should be excluded when resolving
  * dependencies.
- *
- * @author Hans Dockter
  */
 public interface ExcludeRule {
     String GROUP_KEY = "group";
@@ -29,7 +27,7 @@ public interface ExcludeRule {
 
     /**
      * The exact name of the organization or group that should be excluded.
-     * */
+      */
     String getGroup();
 
     /**
diff --git a/subprojects/core/src/main/groovy/org/gradle/api/artifacts/ExcludeRuleContainer.java b/subprojects/core/src/main/groovy/org/gradle/api/artifacts/ExcludeRuleContainer.java
index 58e0425..aef1b20 100644
--- a/subprojects/core/src/main/groovy/org/gradle/api/artifacts/ExcludeRuleContainer.java
+++ b/subprojects/core/src/main/groovy/org/gradle/api/artifacts/ExcludeRuleContainer.java
@@ -20,8 +20,6 @@ import java.util.Set;
 
 /**
  * <p>A container for adding exclude rules for dependencies.</p>
- *
- * @author Hans Dockter
  */
 public interface ExcludeRuleContainer {
     /**
diff --git a/subprojects/core/src/main/groovy/org/gradle/api/artifacts/ExternalDependency.java b/subprojects/core/src/main/groovy/org/gradle/api/artifacts/ExternalDependency.java
index f403492..0a68c02 100644
--- a/subprojects/core/src/main/groovy/org/gradle/api/artifacts/ExternalDependency.java
+++ b/subprojects/core/src/main/groovy/org/gradle/api/artifacts/ExternalDependency.java
@@ -17,8 +17,6 @@ package org.gradle.api.artifacts;
 
 /**
  * <p>An {@code ExternalDependency} is a {@link Dependency} on a source outside the current project hierarchy.</p>
- *
- * @author Hans Dockter
  */
 public interface ExternalDependency extends ModuleDependency, ModuleVersionSelector {
     /**
diff --git a/subprojects/core/src/main/groovy/org/gradle/api/artifacts/ExternalModuleDependency.java b/subprojects/core/src/main/groovy/org/gradle/api/artifacts/ExternalModuleDependency.java
index 356be1c..8759431 100644
--- a/subprojects/core/src/main/groovy/org/gradle/api/artifacts/ExternalModuleDependency.java
+++ b/subprojects/core/src/main/groovy/org/gradle/api/artifacts/ExternalModuleDependency.java
@@ -17,8 +17,6 @@ package org.gradle.api.artifacts;
 
 /**
  * <p>A {@code ModuleDependency} is a {@link Dependency} on a module outside the current project hierarchy.</p>
- *
- * @author Hans Dockter
  */
 public interface ExternalModuleDependency extends ExternalDependency {
     /**
diff --git a/subprojects/core/src/main/groovy/org/gradle/api/artifacts/Module.java b/subprojects/core/src/main/groovy/org/gradle/api/artifacts/Module.java
index 484ca60..9bf97ca 100644
--- a/subprojects/core/src/main/groovy/org/gradle/api/artifacts/Module.java
+++ b/subprojects/core/src/main/groovy/org/gradle/api/artifacts/Module.java
@@ -18,8 +18,6 @@ package org.gradle.api.artifacts;
 /**
  * <p>A {@code Module} represents the meta-information about a project which should be used when publishing the
  * module.</p>
- *
- * @author Hans Dockter
  */
 public interface Module {
     public static final String DEFAULT_STATUS = "integration";
diff --git a/subprojects/core/src/main/groovy/org/gradle/api/artifacts/ProjectDependency.java b/subprojects/core/src/main/groovy/org/gradle/api/artifacts/ProjectDependency.java
index 364da14..1e27198 100644
--- a/subprojects/core/src/main/groovy/org/gradle/api/artifacts/ProjectDependency.java
+++ b/subprojects/core/src/main/groovy/org/gradle/api/artifacts/ProjectDependency.java
@@ -16,12 +16,12 @@
 package org.gradle.api.artifacts;
 
 import org.gradle.api.Project;
+import org.gradle.api.internal.HasInternalProtocol;
 
 /**
  * <p>A {@code ProjectDependency} is a {@link Dependency} on another project in the current project hierarchy.</p>
- *
- * @author Hans Dockter
  */
+ at HasInternalProtocol
 public interface ProjectDependency extends ModuleDependency, SelfResolvingDependency {
     /**
      * Returns the project associated with this project dependency.
diff --git a/subprojects/core/src/main/groovy/org/gradle/api/artifacts/PublishArtifact.java b/subprojects/core/src/main/groovy/org/gradle/api/artifacts/PublishArtifact.java
index 0bc99d4..117285b 100644
--- a/subprojects/core/src/main/groovy/org/gradle/api/artifacts/PublishArtifact.java
+++ b/subprojects/core/src/main/groovy/org/gradle/api/artifacts/PublishArtifact.java
@@ -23,8 +23,6 @@ import java.util.Date;
 
 /**
  * <p>A {@code PublishArtifact} is an artifact produced by a project.</p>
- *
- * @author Hans Dockter
  */
 public interface PublishArtifact extends Buildable {
     /**
@@ -36,7 +34,7 @@ public interface PublishArtifact extends Buildable {
 
     /**
      * Returns the extension of this published artifact. Often the extension is the same as the type,
-     * but sometimes this is not the case. For example for an ivy xml module descriptor, the type is
+     * but sometimes this is not the case. For example for an ivy XML module descriptor, the type is
      * <em>ivy</em> and the extension is <em>xml</em>.
      *
      * @return The extension. Never null.
@@ -45,7 +43,7 @@ public interface PublishArtifact extends Buildable {
 
     /**
      * Returns the type of the published artifact. Often the type is the same as the extension,
-     * but sometimes this is not the case. For example for an ivy xml module descriptor, the type is
+     * but sometimes this is not the case. For example for an ivy XML module descriptor, the type is
      * <em>ivy</em> and the extension is <em>xml</em>.
      *
      * @return The type. Never null.
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 553395e..d98d975 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
@@ -21,8 +21,6 @@ import org.gradle.api.internal.Contextual;
 
 /**
  * <p>A <code>ResolveException</code> is thrown when a dependency configuration cannot be resolved for some reason.</p>
- *
- * @author Hans Dockter
  */
 @Contextual
 public class ResolveException extends AbstractMultiCauseException {
diff --git a/subprojects/core/src/main/groovy/org/gradle/api/artifacts/ResolvedArtifact.java b/subprojects/core/src/main/groovy/org/gradle/api/artifacts/ResolvedArtifact.java
index 486fa6f..13d50ce 100644
--- a/subprojects/core/src/main/groovy/org/gradle/api/artifacts/ResolvedArtifact.java
+++ b/subprojects/core/src/main/groovy/org/gradle/api/artifacts/ResolvedArtifact.java
@@ -19,8 +19,6 @@ import java.io.File;
 
 /**
  * Information about a resolved artifact.
- * 
- * @author Hans Dockter
  */
 public interface ResolvedArtifact {
     File getFile();
diff --git a/subprojects/core/src/main/groovy/org/gradle/api/artifacts/ResolvedDependency.java b/subprojects/core/src/main/groovy/org/gradle/api/artifacts/ResolvedDependency.java
index 2fc35c4..22fc196 100644
--- a/subprojects/core/src/main/groovy/org/gradle/api/artifacts/ResolvedDependency.java
+++ b/subprojects/core/src/main/groovy/org/gradle/api/artifacts/ResolvedDependency.java
@@ -19,8 +19,6 @@ import java.util.Set;
 
 /**
  * Information about a resolved dependency.
- *
- * @author Hans Dockter
  */
 public interface ResolvedDependency {
     /**
diff --git a/subprojects/core/src/main/groovy/org/gradle/api/artifacts/UnknownRepositoryException.java b/subprojects/core/src/main/groovy/org/gradle/api/artifacts/UnknownRepositoryException.java
index 7908ced..120b6bc 100644
--- a/subprojects/core/src/main/groovy/org/gradle/api/artifacts/UnknownRepositoryException.java
+++ b/subprojects/core/src/main/groovy/org/gradle/api/artifacts/UnknownRepositoryException.java
@@ -19,8 +19,6 @@ import org.gradle.api.UnknownDomainObjectException;
 
 /**
  * An {@code UnknownRepositoryException} is thrown when a repository referenced by name cannot be found.
- *
- * @author Hans Dockter
  */
 public class UnknownRepositoryException extends UnknownDomainObjectException {
     public UnknownRepositoryException(String message) {
diff --git a/subprojects/core/src/main/groovy/org/gradle/api/artifacts/dsl/ArtifactHandler.java b/subprojects/core/src/main/groovy/org/gradle/api/artifacts/dsl/ArtifactHandler.java
index 376cb32..ed74047 100644
--- a/subprojects/core/src/main/groovy/org/gradle/api/artifacts/dsl/ArtifactHandler.java
+++ b/subprojects/core/src/main/groovy/org/gradle/api/artifacts/dsl/ArtifactHandler.java
@@ -67,8 +67,6 @@ import org.gradle.api.artifacts.PublishArtifact;
  *   schema schemaJar
  * }
  * </pre>
- *
- * @author Hans Dockter
  */
 public interface ArtifactHandler {
     /**
diff --git a/subprojects/core/src/main/groovy/org/gradle/api/artifacts/dsl/ComponentMetadataHandler.java b/subprojects/core/src/main/groovy/org/gradle/api/artifacts/dsl/ComponentMetadataHandler.java
new file mode 100644
index 0000000..39f21af
--- /dev/null
+++ b/subprojects/core/src/main/groovy/org/gradle/api/artifacts/dsl/ComponentMetadataHandler.java
@@ -0,0 +1,54 @@
+/*
+ * Copyright 2013 the original author or authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.gradle.api.artifacts.dsl;
+
+import org.gradle.api.Action;
+import org.gradle.api.Incubating;
+import org.gradle.api.artifacts.ComponentMetadataDetails;
+
+/**
+ * Allows to modify the metadata of depended-on software components.
+ *
+ * <p> Example:
+ * <pre autoTested=''>
+ * dependencies {
+ *     components {
+ *         eachComponent { ComponentMetadataDetails details ->
+ *             if (details.id.group == "org.foo") {
+ *                 def version = details.id.version
+ *                 // assuming status is last part of version string
+ *                 details.status = version.substring(version.lastIndexOf("-") + 1)
+ *                 details.statusScheme = ["bronze", "silver", "gold", "platinum"]
+ *             }
+ *         }
+ *     }
+ * }
+ * </pre>
+ *
+ * @since 1.8
+ */
+ at Incubating
+public interface ComponentMetadataHandler {
+    /**
+     * Adds a rule to modify the metadata of depended-on software components.
+     * For example, this allows to set a component's status and status scheme
+     * from within the build script, overriding any value specified in the
+     * component descriptor.
+     *
+     * @param rule the rule to be added
+     */
+    void eachComponent(Action<? super ComponentMetadataDetails> rule);
+}
diff --git a/subprojects/core/src/main/groovy/org/gradle/api/artifacts/dsl/DependencyHandler.java b/subprojects/core/src/main/groovy/org/gradle/api/artifacts/dsl/DependencyHandler.java
index 1cef039..b25b3f1 100644
--- a/subprojects/core/src/main/groovy/org/gradle/api/artifacts/dsl/DependencyHandler.java
+++ b/subprojects/core/src/main/groovy/org/gradle/api/artifacts/dsl/DependencyHandler.java
@@ -16,6 +16,7 @@
 package org.gradle.api.artifacts.dsl;
 
 import groovy.lang.Closure;
+import org.gradle.api.Incubating;
 import org.gradle.api.artifacts.Dependency;
 
 import java.util.Map;
@@ -111,8 +112,10 @@ import java.util.Map;
  *   compile(group: 'org.myorg', name: 'someLib', version:'1.0') {
  *     //explicitly adding the dependency artifact:
  *     artifact {
+ *       //useful when some artifact properties unconventional
  *       name = 'someArtifact' //artifact name different than module name
- *       type = 'jar'
+ *       extension = 'someExt'
+ *       type = 'someType'
  *       classifier = 'someClassifier'
  *     }
  *   }
@@ -213,11 +216,11 @@ import java.util.Map;
  * <pre autoTested=''>
  * //Our Gradle plugin is written in groovy
  * apply plugin: 'groovy'
- * //now we can use 'groovy' and 'compile' configuration for declaring dependencies
+ * //now we can use the 'compile' configuration for declaring dependencies
  *
  * dependencies {
- *   //we will use groovy that ships with Gradle:
- *   groovy localGroovy()
+ *   //we will use the Groovy version that ships with Gradle:
+ *   compile localGroovy()
  *
  *   //our plugin requires Gradle API interfaces and classes to compile:
  *   compile gradleApi()
@@ -236,8 +239,6 @@ import java.util.Map;
  *
  * The module notation is the same as the dependency notations described above, except that the classifier property is
  * not available. Client modules are represented using a {@link org.gradle.api.artifacts.ClientModule}.
- *
- * @author Hans Dockter
  */
 public interface DependencyHandler {
     /**
@@ -318,4 +319,26 @@ public interface DependencyHandler {
      * @return The dependency.
      */
     Dependency localGroovy();
+
+    /**
+     * Returns the component metadata handler for this project. The returned handler can be used for adding rules
+     * that modify the metadata of depended-on software components.
+     *
+     * @return the component metadata handler for this project
+     * @since 1.8
+     */
+    @Incubating
+    ComponentMetadataHandler getComponents();
+
+    /**
+     * Configures module metadata for this project.
+     *
+     * <p>This method executes the given closure against the {@link org.gradle.api.artifacts.dsl.ComponentMetadataHandler} for this project. The {@link
+     * org.gradle.api.artifacts.dsl.ComponentMetadataHandler} is passed to the closure as the closure's delegate.
+     *
+     * @param configureClosure the closure to use to configure module metadata
+     * @since 1.8
+     */
+    @Incubating
+    void components(Closure configureClosure);
 }
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 1c0c851..1e777ea 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.gradle.api.Action;
+import org.gradle.api.Incubating;
 import org.gradle.api.artifacts.ArtifactRepositoryContainer;
 import org.gradle.api.artifacts.repositories.FlatDirectoryArtifactRepository;
 import org.gradle.api.artifacts.repositories.IvyArtifactRepository;
@@ -27,8 +28,6 @@ import java.util.Map;
 
 /**
  * A {@code RepositoryHandler} manages a set of repositories, allowing repositories to be defined and queried.
- *
- * @author Hans Dockter
  */
 public interface RepositoryHandler extends ArtifactRepositoryContainer {
 
@@ -52,7 +51,7 @@ public interface RepositoryHandler extends ArtifactRepositoryContainer {
      * </table>
      *
      * <p>Examples:
-     * <pre>
+     * <pre autoTested=''>
      * repositories {
      *     flatDir name: 'libs', dirs: "$projectDir/libs"
      *     flatDir dirs: ["$projectDir/libs1", "$projectDir/libs2"]
@@ -84,6 +83,50 @@ public interface RepositoryHandler extends ArtifactRepositoryContainer {
     FlatDirectoryArtifactRepository flatDir(Action<? super FlatDirectoryArtifactRepository> action);
 
     /**
+     * Adds a repository which looks in Bintray's JCenter repository for dependencies.
+     * <p>
+     * The URL used to access this repository is {@literal "http://jcenter.bintray.com/"}.
+     * The behavior of this resolver is otherwise the same as the ones added by {@link #maven(org.gradle.api.Action)}.
+     * <p>
+     * Examples:
+     * <pre autoTested="">
+     * repositories {
+     *   jcenter {
+     *     artifactUrls = ["http://www.mycompany.com/artifacts1", "http://www.mycompany.com/artifacts2"]
+     *   }
+     *   jcenter {
+     *     name = "nonDefaultName"
+     *     artifactUrls = ["http://www.mycompany.com/artifacts1"]
+     *   }
+     * }
+     * </pre>
+     *
+     * @param action a configuration action
+     * @return the added repository
+     */
+    @Incubating
+    MavenArtifactRepository jcenter(Action<? super MavenArtifactRepository> action);
+
+    /**
+     * Adds a repository which looks in Bintray's JCenter repository for dependencies.
+     * <p>
+     * The URL used to access this repository is {@literal "http://jcenter.bintray.com/"}.
+     * The behavior of this resolver is otherwise the same as the ones added by {@link #mavenCentral()}.
+     * <p>
+     * Examples:
+     * <pre autoTested="">
+     * repositories {
+     *     jcenter()
+     * }
+     * </pre>
+     *
+     * @return the added resolver
+     * @see #jcenter(Action)
+     */
+    @Incubating
+    MavenArtifactRepository jcenter();
+
+    /**
      * Adds a repository which looks in the Maven central repository for dependencies. The URL used to access this repository is
      * {@value org.gradle.api.artifacts.ArtifactRepositoryContainer#MAVEN_CENTRAL_URL}. The behavior of this resolver
      * is otherwise the same as the ones added by {@link #mavenRepo(java.util.Map)}.
@@ -99,13 +142,13 @@ public interface RepositoryHandler extends ArtifactRepositoryContainer {
      * must be unique amongst a repository group.
      * </td></tr>
      * <tr><td><code>artifactUrls</code></td>
-     *     <td>A single jar repository or a collection of jar repositories containing additional artifacts not found in the maven central repository.
-     * But be aware that the POM must exist in maven central.
+     *     <td>A single jar repository or a collection of jar repositories containing additional artifacts not found in the Maven central repository.
+     * But be aware that the POM must exist in Maven central.
      * The provided values are evaluated as per {@link org.gradle.api.Project#uri(Object)}.</td></tr>
      * </table>
      *
      * <p>Examples:
-     * <pre>
+     * <pre autoTested="">
      * repositories {
      *     mavenCentral artifactUrls: ["http://www.mycompany.com/artifacts1", "http://www.mycompany.com/artifacts2"]
      *     mavenCentral name: "nonDefaultName", artifactUrls: ["http://www.mycompany.com/artifacts1"]
@@ -124,7 +167,7 @@ public interface RepositoryHandler extends ArtifactRepositoryContainer {
      * {@value org.gradle.api.artifacts.ArtifactRepositoryContainer#DEFAULT_MAVEN_CENTRAL_REPO_NAME}.
      *
      * <p>Examples:
-     * <pre>
+     * <pre autoTested="">
      * repositories {
      *     mavenCentral()
      * }
@@ -141,7 +184,7 @@ public interface RepositoryHandler extends ArtifactRepositoryContainer {
      * {@value org.gradle.api.artifacts.ArtifactRepositoryContainer#DEFAULT_MAVEN_LOCAL_REPO_NAME}.
      *
      * <p>Examples:
-     * <pre>
+     * <pre autoTested="">
      * repositories {
      *     mavenLocal()
      * }
@@ -158,10 +201,10 @@ public interface RepositoryHandler extends ArtifactRepositoryContainer {
      * to a Maven repository, have a look at {@link org.gradle.api.plugins.MavenRepositoryHandlerConvention#mavenDeployer(java.util.Map)} or
      * {@link org.gradle.api.plugins.MavenRepositoryHandlerConvention#mavenInstaller(java.util.Map)}.
      *
-     * By default the repository accepts to resolve artifacts without a pom. The repository always looks first for the pom
+     * By default the repository accepts to resolve artifacts without a POM. The repository always looks first for the POM
      * in the root repository. It then looks for the artifact in the root repository. Sometimes the artifact
-     * lives in a different repository than the pom. In such a case you can specify further locations to look for an artifact.
-     * But be aware that the pom is only looked up in the root repository.
+     * lives in a different repository than the POM. In such a case you can specify further locations to look for an artifact.
+     * But be aware that the POM is only looked up in the root repository.
      *
      * The following parameter are accepted as keys for the map:
      *
@@ -197,18 +240,22 @@ public interface RepositoryHandler extends ArtifactRepositoryContainer {
      *
      * @param args The argument to create the repository
      * @return the added repository
+     * @deprecated Use {@link #maven(groovy.lang.Closure)} instead.
      */
     @SuppressWarnings("JavadocReference")
+    @Deprecated
     DependencyResolver mavenRepo(Map<String, ?> args);
 
     /**
      * Adds a repository which is Maven compatible.
-     * 
+     *
      * @param args The argument to create the repository
      * @param configClosure Further configuration of the dependency resolver
      * @return The created dependency resolver
      * @see #mavenRepo(java.util.Map)
+     * @deprecated Use {@link #maven(groovy.lang.Closure)} instead.
      */
+    @Deprecated
     DependencyResolver mavenRepo(Map<String, ?> args, Closure configClosure);
 
     /**
diff --git a/subprojects/core/src/main/groovy/org/gradle/api/artifacts/repositories/IvyArtifactRepository.java b/subprojects/core/src/main/groovy/org/gradle/api/artifacts/repositories/IvyArtifactRepository.java
index 9eb30de..bf92523 100644
--- a/subprojects/core/src/main/groovy/org/gradle/api/artifacts/repositories/IvyArtifactRepository.java
+++ b/subprojects/core/src/main/groovy/org/gradle/api/artifacts/repositories/IvyArtifactRepository.java
@@ -106,7 +106,7 @@ public interface IvyArtifactRepository extends ArtifactRepository, Authenticatio
      *     <li>Artifacts: <code>$baseUri/{@value #MAVEN_ARTIFACT_PATTERN}</code></li>
      *     <li>Ivy: <code>$baseUri/{@value #MAVEN_IVY_PATTERN}</code></li>
      * </ul>
-     * Following the maven convention, the 'organisation' value is further processed by replacing '.' with '/'.
+     * Following the Maven convention, the 'organisation' value is further processed by replacing '.' with '/'.
      *
      * <h4>'pattern'</h4>
      * A repository layout that allows custom patterns to be defined. eg:
diff --git a/subprojects/core/src/main/groovy/org/gradle/api/component/SoftwareComponent.java b/subprojects/core/src/main/groovy/org/gradle/api/component/SoftwareComponent.java
index a83c2d2..8bac404 100644
--- a/subprojects/core/src/main/groovy/org/gradle/api/component/SoftwareComponent.java
+++ b/subprojects/core/src/main/groovy/org/gradle/api/component/SoftwareComponent.java
@@ -18,10 +18,11 @@ package org.gradle.api.component;
 
 import org.gradle.api.Incubating;
 import org.gradle.api.Named;
+import org.gradle.api.internal.HasInternalProtocol;
 
 /**
  * A software component produced by a Gradle software project.
  */
- at Incubating
+ at Incubating @HasInternalProtocol
 public interface SoftwareComponent extends Named {
 }
diff --git a/subprojects/core/src/main/groovy/org/gradle/api/dsl/ConventionProperty.java b/subprojects/core/src/main/groovy/org/gradle/api/dsl/ConventionProperty.java
index 12c0569..c33a103 100644
--- a/subprojects/core/src/main/groovy/org/gradle/api/dsl/ConventionProperty.java
+++ b/subprojects/core/src/main/groovy/org/gradle/api/dsl/ConventionProperty.java
@@ -70,7 +70,5 @@ package org.gradle.api.dsl;
  * 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 ConventionProperty {}
\ No newline at end of file
diff --git a/subprojects/core/src/main/groovy/org/gradle/api/execution/TaskExecutionAdapter.java b/subprojects/core/src/main/groovy/org/gradle/api/execution/TaskExecutionAdapter.java
index c4091c1..3f4ecce 100644
--- a/subprojects/core/src/main/groovy/org/gradle/api/execution/TaskExecutionAdapter.java
+++ b/subprojects/core/src/main/groovy/org/gradle/api/execution/TaskExecutionAdapter.java
@@ -1,5 +1,5 @@
 /*
- * Copyright 2009 the original author or authors.
+ * Copyright 2013 the original author or authors.
  *
  * Licensed under the Apache License, Version 2.0 (the "License");
  * you may not use this file except in compliance with the License.
diff --git a/subprojects/core/src/main/groovy/org/gradle/api/file/CopySourceSpec.java b/subprojects/core/src/main/groovy/org/gradle/api/file/CopySourceSpec.java
index 2ef2794..4ca0cca 100644
--- a/subprojects/core/src/main/groovy/org/gradle/api/file/CopySourceSpec.java
+++ b/subprojects/core/src/main/groovy/org/gradle/api/file/CopySourceSpec.java
@@ -19,8 +19,6 @@ import groovy.lang.Closure;
 
 /**
  * Specifies sources for a file copy.
- *
- * @author Steve Appling
  */
 public interface CopySourceSpec {
     /**
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 256a4a0..03f5891 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
@@ -17,11 +17,14 @@ 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.Incubating;
+import org.gradle.api.Nullable;
+import org.gradle.api.internal.HasInternalProtocol;
 import org.gradle.api.specs.Spec;
+import org.gradle.api.tasks.util.PatternFilterable;
 
-import java.util.Map;
 import java.io.FilterReader;
+import java.util.Map;
 import java.util.regex.Pattern;
 
 /**
@@ -62,10 +65,10 @@ import java.util.regex.Pattern;
  * 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()
  */
+ at HasInternalProtocol
 public interface CopySpec extends CopySourceSpec, CopyProcessingSpec, PatternFilterable {
     /**
      * Specifies whether case-sensitive pattern matching should be used.
@@ -96,7 +99,51 @@ public interface CopySpec extends CopySourceSpec, CopyProcessingSpec, PatternFil
     void setIncludeEmptyDirs(boolean includeEmptyDirs);
 
     /**
+     * Returns the strategy to use when trying to copy more than one file to the same destination.
+     * <p>
+     * The value can be set with a case insensitive string of the enum value (e.g. {@code 'exclude'} for {@link DuplicatesStrategy#EXCLUDE}).
+     * <p>
+     * This strategy can be overridden for individual files by using {@link #eachFile(org.gradle.api.Action)} or {@link #filesMatching(String, org.gradle.api.Action)}.
+     *
+     * @return the strategy to use for files included by this copy spec.
+     * @see DuplicatesStrategy
+     */
+    @Incubating
+    DuplicatesStrategy getDuplicatesStrategy();
+
+    /**
+     * The strategy to use when trying to copy more than one file to the same destination. Set to {@code null} to use the default strategy, which is inherited
+     * from the parent copy spec, if any, or {@link DuplicatesStrategy#INCLUDE} if this copy spec has no parent.
+     */
+    @Incubating
+    void setDuplicatesStrategy(@Nullable DuplicatesStrategy strategy);
+
+    /**
+     * Configure the {@link org.gradle.api.file.FileCopyDetails} for each file whose path matches the specified Ant-style pattern.
+     * This is equivalent to using eachFile() and selectively applying a configuration based on the file's path.
+     *
+     * @param pattern Ant-style pattern used to match against files' relative paths
+     * @param action action called for the FileCopyDetails of each file matching pattern
+     * @return this
+     */
+    @Incubating
+    CopySpec filesMatching(String pattern, Action<? super FileCopyDetails> action);
+
+    /**
+     * Configure the {@link org.gradle.api.file.FileCopyDetails} for each file whose path does not match the specified
+     * Ant-style pattern. This is equivalent to using eachFile() and selectively applying a configuration based on the
+     * file's path.
+     *
+     * @param pattern Ant-style pattern used to match against files' relative paths
+     * @param action action called for the FileCopyDetails of each file that does not match pattern
+     * @return this
+     */
+    @Incubating
+    CopySpec filesNotMatching(String pattern, Action<? super FileCopyDetails> action);
+
+    /**
      * Adds the given specs as a child of this spec.
+     *
      * @param sourceSpecs The specs to add
      * @return this
      */
diff --git a/subprojects/core/src/main/groovy/org/gradle/api/file/DeleteAction.java b/subprojects/core/src/main/groovy/org/gradle/api/file/DeleteAction.java
index 4be5015..0a70840 100644
--- a/subprojects/core/src/main/groovy/org/gradle/api/file/DeleteAction.java
+++ b/subprojects/core/src/main/groovy/org/gradle/api/file/DeleteAction.java
@@ -17,8 +17,6 @@ package org.gradle.api.file;
 
 /**
  * Deletes files and directories.
- *
- * @author Hans Dockter
  */
 public interface DeleteAction {
     /**
diff --git a/subprojects/core/src/main/groovy/org/gradle/api/file/DuplicateFileCopyingException.java b/subprojects/core/src/main/groovy/org/gradle/api/file/DuplicateFileCopyingException.java
new file mode 100644
index 0000000..9aa08d3
--- /dev/null
+++ b/subprojects/core/src/main/groovy/org/gradle/api/file/DuplicateFileCopyingException.java
@@ -0,0 +1,35 @@
+/*
+ * Copyright 2013 the original author or authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF 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.GradleException;
+
+/**
+ * Thrown when more than one file with the same relative path name is to be copied
+ * and the {@link DuplicatesStrategy} is set to DuplicatesStrategy.FAIL
+ */
+public class DuplicateFileCopyingException extends GradleException {
+
+    public DuplicateFileCopyingException() {
+
+    }
+
+    public DuplicateFileCopyingException(String desc) {
+        super(desc);
+    }
+
+}
diff --git a/subprojects/core/src/main/groovy/org/gradle/api/file/DuplicatesStrategy.java b/subprojects/core/src/main/groovy/org/gradle/api/file/DuplicatesStrategy.java
new file mode 100644
index 0000000..c36600c
--- /dev/null
+++ b/subprojects/core/src/main/groovy/org/gradle/api/file/DuplicatesStrategy.java
@@ -0,0 +1,59 @@
+/*
+ * Copyright 2013 the original author or authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF 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.Incubating;
+
+/**
+ * Strategies for dealing with the potential creation of duplicate files for or archive entries.
+ */
+ at Incubating
+public enum DuplicatesStrategy {
+
+    /**
+     * Do not attempt to prevent duplicates.
+     * <p>
+     * If the destination of the operation supports duplicates (e.g. zip files) then a duplicate entry will be created.
+     * If the destination does not support duplicates, the existing destination entry will be overridden with the duplicate.
+     */
+    INCLUDE,
+
+    /**
+     * Do not allow duplicates by ignoring subsequent items to be created at the same path.
+     * <p>
+     * If an attempt is made to create a duplicate file/entry during an operation, ignore the item.
+     * This will leave the file/entry that was first copied/created in place.
+     */
+    EXCLUDE,
+
+    /**
+     * Do not attempt to prevent duplicates, but log a warning message when multiple items 
+     * are to be created at the same path.
+     * <p>
+     * This behaves exactly as INCLUDE otherwise.
+     */
+    WARN,
+
+    /**
+     * Throw a {@link DuplicateFileCopyingException} when subsequent items are to be created at the same path.
+     * <p> 
+     * Use this strategy when duplicates are an error condition that should cause the build to fail.
+     */
+    FAIL
+
+
+}
diff --git a/subprojects/core/src/main/groovy/org/gradle/api/file/EmptyFileVisitor.java b/subprojects/core/src/main/groovy/org/gradle/api/file/EmptyFileVisitor.java
index 4c386df..1dd32e9 100644
--- a/subprojects/core/src/main/groovy/org/gradle/api/file/EmptyFileVisitor.java
+++ b/subprojects/core/src/main/groovy/org/gradle/api/file/EmptyFileVisitor.java
@@ -18,8 +18,6 @@ package org.gradle.api.file;
 /**
  * The EmptyFileVisitor can be extends by implementations that only require to implement one of the 2 visit methods
  * (dir or file). This is just to limit the amount of code clutter when not both visit methods need to be implemented.
- *
- * @author Tom Eyckmans
  */
 public class EmptyFileVisitor implements FileVisitor {
 
diff --git a/subprojects/core/src/main/groovy/org/gradle/api/file/FileCopyDetails.java b/subprojects/core/src/main/groovy/org/gradle/api/file/FileCopyDetails.java
index 59a2ab9..a458328 100644
--- a/subprojects/core/src/main/groovy/org/gradle/api/file/FileCopyDetails.java
+++ b/subprojects/core/src/main/groovy/org/gradle/api/file/FileCopyDetails.java
@@ -15,6 +15,10 @@
  */
 package org.gradle.api.file;
 
+import org.gradle.api.Incubating;
+import org.gradle.api.internal.HasInternalProtocol;
+import org.gradle.api.NonExtensible;
+
 /**
  * <p>Provides details about a file or directory about to be copied, and allows some aspects of the destination file to
  * be modified.</p>
@@ -22,6 +26,8 @@ package org.gradle.api.file;
  * <p>Using this interface, you can change the destination path of the file, filter the content of the file, or exclude
  * the file from the result entirely.</p>
  */
+ at HasInternalProtocol
+ at NonExtensible
 public interface FileCopyDetails extends FileTreeElement, ContentFilterable {
     /**
      * Excludes this file from the copy.
@@ -55,4 +61,22 @@ public interface FileCopyDetails extends FileTreeElement, ContentFilterable {
      * @param mode the Unix permissions, e.g. {@code 0644}.
      */
     void setMode(int mode);
+
+    /**
+     * The strategy to use if there is already a file at this file's destination.
+     */
+    @Incubating
+    void setDuplicatesStrategy(DuplicatesStrategy strategy);
+
+    /**
+     * The strategy to use if there is already a file at this file's destination.
+     * <p>
+     * The value can be set with a case insensitive string of the enum value (e.g. {@code 'exclude'} for {@link DuplicatesStrategy#EXCLUDE}).
+     *
+     * @see DuplicatesStrategy
+     * @return the strategy to use for this file.
+     */
+    @Incubating
+    DuplicatesStrategy getDuplicatesStrategy();
+
 }
diff --git a/subprojects/core/src/main/groovy/org/gradle/api/file/FileTree.java b/subprojects/core/src/main/groovy/org/gradle/api/file/FileTree.java
index 92ba383..b9b1764 100644
--- a/subprojects/core/src/main/groovy/org/gradle/api/file/FileTree.java
+++ b/subprojects/core/src/main/groovy/org/gradle/api/file/FileTree.java
@@ -33,7 +33,7 @@ public interface FileTree extends FileCollection {
      * that any changes to this tree are reflected in the filtered tree.</p>
      *
      * <p>The given closure is used to configure the filter. A {@link org.gradle.api.tasks.util.PatternFilterable} is
-     * passed to the closure as it's delegate. Only files which match the specified include patterns will be included in
+     * passed to the closure as its delegate. Only files which match the specified include patterns will be included in
      * the filtered tree. Any files which match the specified exclude patterns will be excluded from the filtered
      * tree.</p>
      *
diff --git a/subprojects/core/src/main/groovy/org/gradle/api/file/FileVisitor.java b/subprojects/core/src/main/groovy/org/gradle/api/file/FileVisitor.java
index 0972d74..e789709 100644
--- a/subprojects/core/src/main/groovy/org/gradle/api/file/FileVisitor.java
+++ b/subprojects/core/src/main/groovy/org/gradle/api/file/FileVisitor.java
@@ -17,8 +17,6 @@ package org.gradle.api.file;
 
 /**
  * <p>A {@code FileVisitor} is used to visit each of the files in a {@link FileTree}.</p>
- *
- * @author Steve Appling
  */
 public interface FileVisitor {
     /**
diff --git a/subprojects/core/src/main/groovy/org/gradle/api/file/RelativePath.java b/subprojects/core/src/main/groovy/org/gradle/api/file/RelativePath.java
index 7fa733f..f91e4fb 100644
--- a/subprojects/core/src/main/groovy/org/gradle/api/file/RelativePath.java
+++ b/subprojects/core/src/main/groovy/org/gradle/api/file/RelativePath.java
@@ -29,8 +29,6 @@ import java.util.ListIterator;
  * and target file path when copying files.</p>
  *
  * <p>{@code RelativePath} instances are immutable.</p>
- *
- * @author Steve Appling
  */
 public class RelativePath implements Serializable {
     private final boolean endsWithFile;
diff --git a/subprojects/core/src/main/groovy/org/gradle/api/initialization/ProjectDescriptor.java b/subprojects/core/src/main/groovy/org/gradle/api/initialization/ProjectDescriptor.java
index cffbe5e..7e763bd 100644
--- a/subprojects/core/src/main/groovy/org/gradle/api/initialization/ProjectDescriptor.java
+++ b/subprojects/core/src/main/groovy/org/gradle/api/initialization/ProjectDescriptor.java
@@ -25,8 +25,6 @@ import java.util.Set;
  * <p> A {@code ProjectDescriptor} is created when you add a project to the build from the settings script, using {@link
  * Settings#include(String[])} or {@link Settings#includeFlat(String[])}. You can access the descriptors using one of
  * the lookup methods on the {@link Settings} object.</p>
- *
- * @author Hans Dockter
  */
 public interface ProjectDescriptor {
     /**
diff --git a/subprojects/core/src/main/groovy/org/gradle/api/initialization/Settings.java b/subprojects/core/src/main/groovy/org/gradle/api/initialization/Settings.java
index ad79d28..97ffeb2 100644
--- a/subprojects/core/src/main/groovy/org/gradle/api/initialization/Settings.java
+++ b/subprojects/core/src/main/groovy/org/gradle/api/initialization/Settings.java
@@ -19,6 +19,7 @@ package org.gradle.api.initialization;
 import org.gradle.StartParameter;
 import org.gradle.api.UnknownProjectException;
 import org.gradle.api.invocation.Gradle;
+import org.gradle.api.plugins.PluginAware;
 
 import java.io.File;
 
@@ -59,10 +60,8 @@ import java.io.File;
  * <li>Provided on the command-line using the -P option.</li>
  *
  * </ul>
- *
- * @author Hans Dockter
  */
-public interface Settings {
+public interface Settings extends PluginAware {
     /**
      * <p>The default name for the settings file.</p>
      */
diff --git a/subprojects/core/src/main/groovy/org/gradle/api/internal/AbstractClassGenerator.java b/subprojects/core/src/main/groovy/org/gradle/api/internal/AbstractClassGenerator.java
index 897c0a7..ad488f7 100644
--- a/subprojects/core/src/main/groovy/org/gradle/api/internal/AbstractClassGenerator.java
+++ b/subprojects/core/src/main/groovy/org/gradle/api/internal/AbstractClassGenerator.java
@@ -31,9 +31,12 @@ import org.gradle.internal.reflect.Instantiator;
 import java.lang.reflect.Constructor;
 import java.lang.reflect.Modifier;
 import java.util.*;
+import java.util.concurrent.locks.Lock;
+import java.util.concurrent.locks.ReentrantLock;
 
 public abstract class AbstractClassGenerator implements ClassGenerator {
     private static final Map<Class<?>, Map<Class<?>, Class<?>>> GENERATED_CLASSES = new HashMap<Class<?>, Map<Class<?>, Class<?>>>();
+    private static final Lock CACHE_LOCK = new ReentrantLock();
 
     public <T> T newInstance(Class<T> type, Object... parameters) {
         Instantiator instantiator = new DirectInstantiator();
@@ -41,6 +44,15 @@ public abstract class AbstractClassGenerator implements ClassGenerator {
     }
 
     public <T> Class<? extends T> generate(Class<T> type) {
+        try {
+            CACHE_LOCK.lock();
+            return generateUnderLock(type);
+        } finally {
+            CACHE_LOCK.unlock();
+        }
+    }
+
+    private <T> Class<? extends T> generateUnderLock(Class<T> type) {
         Map<Class<?>, Class<?>> cache = GENERATED_CLASSES.get(getClass());
         if (cache == null) {
             // WeakHashMap won't work here. It keeps a strong reference to the mapping value, which is the generated class in this case
diff --git a/subprojects/core/src/main/groovy/org/gradle/api/internal/AbstractNamedDomainObjectContainer.java b/subprojects/core/src/main/groovy/org/gradle/api/internal/AbstractNamedDomainObjectContainer.java
index 2d64cb1..87e0717 100644
--- a/subprojects/core/src/main/groovy/org/gradle/api/internal/AbstractNamedDomainObjectContainer.java
+++ b/subprojects/core/src/main/groovy/org/gradle/api/internal/AbstractNamedDomainObjectContainer.java
@@ -16,9 +16,7 @@
 package org.gradle.api.internal;
 
 import groovy.lang.Closure;
-import org.gradle.api.Named;
-import org.gradle.api.NamedDomainObjectContainer;
-import org.gradle.api.Namer;
+import org.gradle.api.*;
 import org.gradle.internal.reflect.Instantiator;
 import org.gradle.util.ConfigureUtil;
 
@@ -38,7 +36,7 @@ public abstract class AbstractNamedDomainObjectContainer<T> extends DefaultNamed
     protected abstract T doCreate(String name);
 
     public T create(String name) {
-        return create(name, null);
+        return create(name, Actions.doNothing());
     }
 
     public T maybeCreate(String name) {
@@ -50,10 +48,14 @@ public abstract class AbstractNamedDomainObjectContainer<T> extends DefaultNamed
     }
 
     public T create(String name, Closure configureClosure) {
+        return create(name, new ClosureBackedAction<T>(configureClosure));
+    }
+
+    public T create(String name, Action<? super T> configureAction) throws InvalidUserDataException {
         assertCanAdd(name);
         T object = doCreate(name);
         add(object);
-        ConfigureUtil.configure(configureClosure, object);
+        configureAction.execute(object);
         return object;
     }
 
diff --git a/subprojects/core/src/main/groovy/org/gradle/api/internal/AbstractPolymorphicDomainObjectContainer.java b/subprojects/core/src/main/groovy/org/gradle/api/internal/AbstractPolymorphicDomainObjectContainer.java
index 1f5046e..b964534 100644
--- a/subprojects/core/src/main/groovy/org/gradle/api/internal/AbstractPolymorphicDomainObjectContainer.java
+++ b/subprojects/core/src/main/groovy/org/gradle/api/internal/AbstractPolymorphicDomainObjectContainer.java
@@ -123,7 +123,12 @@ public abstract class AbstractPolymorphicDomainObjectContainer<T>
         @Override
         public Object invokeMethod(String name, Object... arguments) throws groovy.lang.MissingMethodException {
             if (isConfigureMethod(name, arguments)) {
-                return ConfigureUtil.configure((Closure) arguments[arguments.length - 1], getByName(name));
+                T element = getByName(name);
+                Object lastArgument = arguments[arguments.length - 1];
+                if (lastArgument instanceof Closure) {
+                    ConfigureUtil.configure((Closure) lastArgument, element);
+                }
+                return element;
             } else {
                 return super.invokeMethod(name, arguments);
             }
@@ -131,6 +136,7 @@ public abstract class AbstractPolymorphicDomainObjectContainer<T>
 
         private boolean isConfigureMethod(String name, Object... arguments) {
             return (arguments.length == 1 && arguments[0] instanceof Closure
+                    || arguments.length == 1 && arguments[0] instanceof Class
                     || arguments.length == 2 && arguments[0] instanceof Class && arguments[1] instanceof Closure)
                     && hasProperty(name);
         }
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 368316c..de0cd73 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
@@ -24,6 +24,7 @@ import org.gradle.api.*;
 import org.gradle.api.internal.file.TemporaryFileProvider;
 import org.gradle.api.internal.project.ProjectInternal;
 import org.gradle.api.internal.tasks.*;
+import org.gradle.api.internal.tasks.execution.DefaultTaskExecutionContext;
 import org.gradle.api.internal.tasks.execution.TaskValidator;
 import org.gradle.api.logging.Logger;
 import org.gradle.api.logging.Logging;
@@ -52,9 +53,6 @@ import java.util.List;
 import java.util.Set;
 import java.util.concurrent.Callable;
 
-/**
- * @author Hans Dockter
- */
 public abstract class AbstractTask implements TaskInternal, DynamicObjectAware {
     private static Logger buildLogger = Logging.getLogger(Task.class);
     private static ThreadLocal<TaskInfo> nextInstance = new ThreadLocal<TaskInfo>();
@@ -62,7 +60,7 @@ public abstract class AbstractTask implements TaskInternal, DynamicObjectAware {
 
     private String name;
 
-    private List<Action<? super Task>> actions = new ArrayList<Action<? super Task>>();
+    private List<ContextAwareTaskAction> actions = new ArrayList<ContextAwareTaskAction>();
 
     private String path;
 
@@ -70,6 +68,10 @@ public abstract class AbstractTask implements TaskInternal, DynamicObjectAware {
 
     private DefaultTaskDependency dependencies;
 
+    private DefaultTaskDependency mustRunAfter;
+
+    private DefaultTaskDependency finalizedBy;
+
     private ExtensibleDynamicObject extensibleDynamicObject;
 
     private String description;
@@ -115,6 +117,8 @@ public abstract class AbstractTask implements TaskInternal, DynamicObjectAware {
         path = project.absoluteProjectPath(name);
         state = new TaskStateInternal(toString());
         dependencies = new DefaultTaskDependency(project.getTasks());
+        mustRunAfter = new DefaultTaskDependency(project.getTasks());
+        finalizedBy = new DefaultTaskDependency(project.getTasks());
         services = project.getServices().createFor(this);
         extensibleDynamicObject = new ExtensibleDynamicObject(this, getServices().get(Instantiator.class));
         taskStatusNagger = services.get(TaskStatusNagger.class);
@@ -178,6 +182,10 @@ public abstract class AbstractTask implements TaskInternal, DynamicObjectAware {
         return observableActionList;
     }
 
+    public List<ContextAwareTaskAction> getTaskActions() {
+        return observableActionList;
+    }
+
     public void setActions(final List<Action<? super Task>> actions) {
         taskStatusNagger.nagIfTaskNotInConfigurableState("Task.setActions(Actions<Task>)");
         taskStatusNagger.whileDisabled(new Runnable() {
@@ -272,7 +280,7 @@ public abstract class AbstractTask implements TaskInternal, DynamicObjectAware {
     }
 
     public void executeWithoutThrowingTaskFailure() {
-        executer.execute(this, state);
+        executer.execute(this, state, new DefaultTaskExecutionContext());
     }
 
     public TaskExecuter getExecuter() {
@@ -448,11 +456,11 @@ public abstract class AbstractTask implements TaskInternal, DynamicObjectAware {
         return validators;
     }
 
-    private Action<Task> convertClosureToAction(Closure actionClosure) {
+    private ContextAwareTaskAction convertClosureToAction(Closure actionClosure) {
         return new ClosureTaskAction(actionClosure);
     }
 
-    private Action<Task> wrap(final Action<? super Task> action) {
+    private ContextAwareTaskAction wrap(final Action<? super Task> action) {
         return new TaskActionWrapper(action);
     }
 
@@ -466,13 +474,16 @@ public abstract class AbstractTask implements TaskInternal, DynamicObjectAware {
         }
     }
 
-    private static class ClosureTaskAction implements Action<Task> {
+    private static class ClosureTaskAction implements ContextAwareTaskAction {
         private final Closure closure;
 
         private ClosureTaskAction(Closure closure) {
             this.closure = closure;
         }
 
+        public void contextualise(TaskExecutionContext context) {
+        }
+
         public void execute(Task task) {
             closure.setDelegate(task);
             closure.setResolveStrategy(Closure.DELEGATE_FIRST);
@@ -496,13 +507,19 @@ public abstract class AbstractTask implements TaskInternal, DynamicObjectAware {
         }
     }
 
-    private static class TaskActionWrapper implements Action<Task> {
+    private static class TaskActionWrapper implements ContextAwareTaskAction {
         private final Action<? super Task> action;
 
         public TaskActionWrapper(Action<? super Task> action) {
             this.action = action;
         }
 
+        public void contextualise(TaskExecutionContext context) {
+            if (action instanceof ContextAwareTaskAction) {
+                ((ContextAwareTaskAction) action).contextualise(context);
+            }
+        }
+
         public void execute(Task task) {
             ClassLoader original = Thread.currentThread().getContextClassLoader();
             Thread.currentThread().setContextClassLoader(action.getClass().getClassLoader());
@@ -513,4 +530,34 @@ public abstract class AbstractTask implements TaskInternal, DynamicObjectAware {
             }
         }
     }
+
+    public void setMustRunAfter(Iterable<?> mustRunAfterTasks) {
+        taskStatusNagger.nagIfTaskNotInConfigurableState("Task.setMustRunAfter(Iterable)");
+        mustRunAfter.setValues(mustRunAfterTasks);
+    }
+
+    public Task mustRunAfter(Object... paths) {
+        taskStatusNagger.nagIfTaskNotInConfigurableState("Task.mustRunAfter(Object...)");
+        mustRunAfter.add(paths);
+        return this;
+    }
+
+    public TaskDependency getMustRunAfter() {
+        return mustRunAfter;
+    }
+
+    public void setFinalizedBy(Iterable<?> finalizedByTasks) {
+        taskStatusNagger.nagIfTaskNotInConfigurableState("Task.setFinalizedBy(Iterable)");
+        finalizedBy.setValues(finalizedByTasks);
+    }
+
+    public Task finalizedBy(Object... paths) {
+        taskStatusNagger.nagIfTaskNotInConfigurableState("Task.finalizedBy(Object...)");
+        finalizedBy.add(paths);
+        return this;
+    }
+
+    public TaskDependency getFinalizedBy() {
+        return finalizedBy;
+    }
 }
diff --git a/subprojects/core/src/main/groovy/org/gradle/api/internal/AsmBackedClassGenerator.java b/subprojects/core/src/main/groovy/org/gradle/api/internal/AsmBackedClassGenerator.java
index 0dcb801..9976032 100644
--- a/subprojects/core/src/main/groovy/org/gradle/api/internal/AsmBackedClassGenerator.java
+++ b/subprojects/core/src/main/groovy/org/gradle/api/internal/AsmBackedClassGenerator.java
@@ -16,6 +16,7 @@
 package org.gradle.api.internal;
 
 import groovy.lang.*;
+import org.gradle.api.NonExtensible;
 import org.gradle.api.Transformer;
 import org.gradle.api.plugins.Convention;
 import org.gradle.api.plugins.ExtensionAware;
@@ -34,7 +35,7 @@ import java.util.ArrayList;
 import java.util.List;
 
 public class AsmBackedClassGenerator extends AbstractClassGenerator {
-    private static final JavaMethod<ClassLoader, Class> DEFINE_CLASS_METHOD = JavaMethod.create(ClassLoader.class, Class.class, "defineClass", String.class, byte[].class, Integer.TYPE, Integer.TYPE);
+    private static final JavaMethod<ClassLoader, Class> DEFINE_CLASS_METHOD = JavaReflectionUtil.method(ClassLoader.class, Class.class, "defineClass", String.class, byte[].class, Integer.TYPE, Integer.TYPE);
 
     @Override
     protected <T> ClassBuilder<T> start(Class<T> type) {
@@ -58,6 +59,10 @@ public class AsmBackedClassGenerator extends AbstractClassGenerator {
         private final Type conventionMappingType = Type.getType(ConventionMapping.class);
         private final Type groovyObjectType = Type.getType(GroovyObject.class);
         private final Type conventionType = Type.getType(Convention.class);
+        private final Type extensibleDynamicObjectHelperType = Type.getType(MixInExtensibleDynamicObject.class);
+        private final Type nonExtensibleDynamicObjectHelperType = Type.getType(BeanDynamicObject.class);
+
+        private final boolean extensible;
 
         private ClassBuilderImpl(Class<T> type) {
             this.type = type;
@@ -66,16 +71,22 @@ public class AsmBackedClassGenerator extends AbstractClassGenerator {
             typeName = type.getName() + "_Decorated";
             generatedType = Type.getType("L" + typeName.replaceAll("\\.", "/") + ";");
             superclassType = Type.getType(type);
+
+            extensible = JavaReflectionUtil.getAnnotation(type, NonExtensible.class) == null;
         }
 
         public void startClass(boolean isConventionAware) {
             List<String> interfaceTypes = new ArrayList<String>();
-            if (isConventionAware) {
+            if (isConventionAware && extensible) {
                 interfaceTypes.add(conventionAwareType.getInternalName());
             }
+
+            if (extensible) {
+                interfaceTypes.add(extensionAwareType.getInternalName());
+                interfaceTypes.add(hasConventionType.getInternalName());
+            }
+
             interfaceTypes.add(dynamicObjectAwareType.getInternalName());
-            interfaceTypes.add(extensionAwareType.getInternalName());
-            interfaceTypes.add(hasConventionType.getInternalName());
             interfaceTypes.add(groovyObjectType.getInternalName());
 
             visitor.visit(Opcodes.V1_5, Opcodes.ACC_PUBLIC, generatedType.getInternalName(), null,
@@ -109,7 +120,7 @@ public class AsmBackedClassGenerator extends AbstractClassGenerator {
             // this.super(p0 .. pn)
             methodVisitor.visitVarInsn(Opcodes.ALOAD, 0);
             for (int i = 0; i < constructor.getParameterTypes().length; i++) {
-                methodVisitor.visitVarInsn(Type.getType(constructor.getParameterTypes()[i]).getOpcode(Opcodes.ILOAD), i+1);
+                methodVisitor.visitVarInsn(Type.getType(constructor.getParameterTypes()[i]).getOpcode(Opcodes.ILOAD), i + 1);
             }
             methodVisitor.visitMethodInsn(Opcodes.INVOKESPECIAL, superclassType.getInternalName(), "<init>",
                     methodDescriptor);
@@ -225,108 +236,135 @@ public class AsmBackedClassGenerator extends AbstractClassGenerator {
         }
 
         public void mixInDynamicAware() throws Exception {
-            final Type helperType = Type.getType(MixInExtensibleDynamicObject.class);
 
             // GENERATE private MixInExtensibleDynamicObject dynamicObjectHelper = new MixInExtensibleDynamicObject(this, super.getAsDynamicObject())
 
-            final String fieldSignature = "L" + MixInExtensibleDynamicObject.class.getName().replaceAll("\\.", "/") + ";";
+            Class<?> extensibleObjectFieldType = extensible ? MixInExtensibleDynamicObject.class : BeanDynamicObject.class;
+            final String fieldSignature = Type.getDescriptor(extensibleObjectFieldType);
             visitor.visitField(Opcodes.ACC_PRIVATE, "dynamicObjectHelper", fieldSignature, null, null);
             initDynamicObjectHelper = new MethodCodeBody() {
                 public void add(MethodVisitor visitor) throws Exception {
-                    String helperTypeConstructorDesc = Type.getMethodDescriptor(Type.VOID_TYPE, new Type[]{
-                            Type.getType(Object.class), dynamicObjectType
-                    });
-
-                    // GENERATE dynamicObjectHelper = new MixInExtensibleDynamicObject(this, super.getAsDynamicObject())
+                    generateCreateDynamicObject(visitor);
+                    visitor.visitFieldInsn(Opcodes.PUTFIELD, generatedType.getInternalName(), "dynamicObjectHelper",
+                            fieldSignature);
+                    // END
+                }
+            };
 
-                    visitor.visitVarInsn(Opcodes.ALOAD, 0);
+            // END
 
-                    // GENERATE new MixInExtensibleDynamicObject(this, super.getAsDynamicObject())
-                    visitor.visitTypeInsn(Opcodes.NEW, helperType.getInternalName());
-                    visitor.visitInsn(Opcodes.DUP);
+            if (extensible) {
+                // GENERATE public Convention getConvention() { return dynamicObjectHelper.getConvention(); }
 
-                    visitor.visitVarInsn(Opcodes.ALOAD, 0);
+                addGetter(HasConvention.class.getDeclaredMethod("getConvention"), new MethodCodeBody() {
+                    public void add(MethodVisitor visitor) throws Exception {
 
-                    boolean useInheritedDynamicObject = GroovySystem.getMetaClassRegistry().getMetaClass(type).pickMethod("getAsDynamicObject", new Class[0]) != null;
+                        // GENERATE dynamicObjectHelper.getConvention()
 
-                    if (useInheritedDynamicObject) {
-                        // GENERATE super.getAsDynamicObject()
                         visitor.visitVarInsn(Opcodes.ALOAD, 0);
-                        visitor.visitMethodInsn(Opcodes.INVOKESPECIAL, Type.getType(type).getInternalName(),
-                                "getAsDynamicObject", Type.getMethodDescriptor(dynamicObjectType, new Type[0]));
-                    } else {
-                        // GENERATE null
-                        visitor.visitInsn(Opcodes.ACONST_NULL);
+                        visitor.visitFieldInsn(Opcodes.GETFIELD, generatedType.getInternalName(), "dynamicObjectHelper",
+                                fieldSignature);
+                        String getterDescriptor = Type.getMethodDescriptor(ExtensibleDynamicObject.class.getDeclaredMethod(
+                                "getConvention"));
+                        visitor.visitMethodInsn(Opcodes.INVOKEVIRTUAL, extensibleDynamicObjectHelperType.getInternalName(), "getConvention",
+                                getterDescriptor);
                     }
+                });
 
-                    visitor.visitMethodInsn(Opcodes.INVOKESPECIAL, helperType.getInternalName(), "<init>",
-                            helperTypeConstructorDesc);
-                    // END
+                // END
 
-                    visitor.visitFieldInsn(Opcodes.PUTFIELD, generatedType.getInternalName(), "dynamicObjectHelper",
-                            fieldSignature);
-                    // END
-                }
-            };
+                // GENERATE public ExtensionContainer getExtensions() { return getConvention(); }
 
-            // END
+                addGetter(ExtensionAware.class.getDeclaredMethod("getExtensions"), new MethodCodeBody() {
+                    public void add(MethodVisitor visitor) throws Exception {
 
-            // GENERATE public Convention getConvention() { return dynamicObjectHelper.getConvention(); }
+                        // GENERATE getConvention()
 
-            addGetter(HasConvention.class.getDeclaredMethod("getConvention"), new MethodCodeBody() {
-                public void add(MethodVisitor visitor) throws Exception {
+                        visitor.visitVarInsn(Opcodes.ALOAD, 0);
+                        String getterDescriptor = Type.getMethodDescriptor(ExtensibleDynamicObject.class.getDeclaredMethod(
+                                "getConvention"));
+                        visitor.visitMethodInsn(Opcodes.INVOKEVIRTUAL, generatedType.getInternalName(), "getConvention",
+                                getterDescriptor);
+                    }
+                });
+            }
 
-                    // GENERATE dynamicObjectHelper.getConvention()
+            // END
 
+            // GENERATE public DynamicObject.getAsDynamicObject() { return dynamicObjectHelper; }
+
+            addGetter(DynamicObjectAware.class.getDeclaredMethod("getAsDynamicObject"), new MethodCodeBody() {
+                public void add(MethodVisitor visitor) {
                     visitor.visitVarInsn(Opcodes.ALOAD, 0);
                     visitor.visitFieldInsn(Opcodes.GETFIELD, generatedType.getInternalName(), "dynamicObjectHelper",
                             fieldSignature);
-                    String getterDescriptor = Type.getMethodDescriptor(ExtensibleDynamicObject.class.getDeclaredMethod(
-                            "getConvention"));
-                    visitor.visitMethodInsn(Opcodes.INVOKEVIRTUAL, helperType.getInternalName(), "getConvention",
-                            getterDescriptor);
                 }
             });
 
             // END
+        }
 
-            // GENERATE public ExtensionContainer getExtensions() { return getConvention(); }
+        private void generateCreateDynamicObject(MethodVisitor visitor) {
+            if (extensible) {
 
-            addGetter(ExtensionAware.class.getDeclaredMethod("getExtensions"), new MethodCodeBody() {
-                public void add(MethodVisitor visitor) throws Exception {
+                String helperTypeConstructorDesc = Type.getMethodDescriptor(Type.VOID_TYPE, new Type[]{
+                        Type.getType(Object.class), dynamicObjectType
+                });
 
-                    // GENERATE getConvention()
+                // GENERATE dynamicObjectHelper = new MixInExtensibleDynamicObject(this, super.getAsDynamicObject())
+
+                visitor.visitVarInsn(Opcodes.ALOAD, 0);
+
+                // GENERATE new MixInExtensibleDynamicObject(this, super.getAsDynamicObject())
+                visitor.visitTypeInsn(Opcodes.NEW, extensibleDynamicObjectHelperType.getInternalName());
+                visitor.visitInsn(Opcodes.DUP);
 
+                visitor.visitVarInsn(Opcodes.ALOAD, 0);
+
+                boolean useInheritedDynamicObject = GroovySystem.getMetaClassRegistry().getMetaClass(type).pickMethod("getAsDynamicObject", new Class[0]) != null;
+
+                if (useInheritedDynamicObject) {
+                    // GENERATE super.getAsDynamicObject()
                     visitor.visitVarInsn(Opcodes.ALOAD, 0);
-                    String getterDescriptor = Type.getMethodDescriptor(ExtensibleDynamicObject.class.getDeclaredMethod(
-                            "getConvention"));
-                    visitor.visitMethodInsn(Opcodes.INVOKEVIRTUAL, generatedType.getInternalName(), "getConvention",
-                            getterDescriptor);
+                    visitor.visitMethodInsn(Opcodes.INVOKESPECIAL, Type.getType(type).getInternalName(),
+                            "getAsDynamicObject", Type.getMethodDescriptor(dynamicObjectType, new Type[0]));
+                } else {
+                    // GENERATE null
+                    visitor.visitInsn(Opcodes.ACONST_NULL);
                 }
-            });
 
-            // END
+                visitor.visitMethodInsn(Opcodes.INVOKESPECIAL, extensibleDynamicObjectHelperType.getInternalName(), "<init>",
+                        helperTypeConstructorDesc);
+                // END
+            } else {
+                String helperTypeConstructorDesc = Type.getMethodDescriptor(Type.VOID_TYPE, new Type[]{
+                        Type.getType(Object.class)
+                });
 
-            // GENERATE public DynamicObject.getAsDynamicObject() { return dynamicObjectHelper; }
+                // GENERATE new BeanDynamicObject(this)
 
-            addGetter(DynamicObjectAware.class.getDeclaredMethod("getAsDynamicObject"), new MethodCodeBody() {
-                public void add(MethodVisitor visitor) {
+                visitor.visitVarInsn(Opcodes.ALOAD, 0);
 
-                    // GENERATE dynamicObjectHelper
+                // GENERATE new BeanDynamicObject(this)
+                visitor.visitTypeInsn(Opcodes.NEW, nonExtensibleDynamicObjectHelperType.getInternalName());
+                visitor.visitInsn(Opcodes.DUP);
 
-                    visitor.visitVarInsn(Opcodes.ALOAD, 0);
-                    visitor.visitFieldInsn(Opcodes.GETFIELD, generatedType.getInternalName(), "dynamicObjectHelper",
-                            fieldSignature);
-                }
-            });
+                visitor.visitVarInsn(Opcodes.ALOAD, 0);
 
-            // END
+                visitor.visitMethodInsn(Opcodes.INVOKESPECIAL, nonExtensibleDynamicObjectHelperType.getInternalName(), "<init>",
+                        helperTypeConstructorDesc);
+                // END
+            }
         }
 
         public void mixInConventionAware() throws Exception {
+            if (!extensible) {
+                return;
+            }
+
             // GENERATE private ConventionMapping mapping = new ConventionAwareHelper(this, getConvention())
 
-            final String mappingFieldSignature = "L" + ConventionMapping.class.getName().replaceAll("\\.", "/") + ";";
+            final String mappingFieldSignature = Type.getDescriptor(ConventionMapping.class);
             final String getConventionDesc = Type.getMethodDescriptor(conventionType, new Type[0]);
 
             visitor.visitField(Opcodes.ACC_PRIVATE, "mapping", mappingFieldSignature, null, null);
@@ -395,7 +433,7 @@ public class AsmBackedClassGenerator extends AbstractClassGenerator {
 
             // GENERATE private MetaClass metaClass = GroovySystem.getMetaClassRegistry().getMetaClass(getClass())
 
-            final String metaClassFieldSignature = "L" + MetaClass.class.getName().replaceAll("\\.", "/") + ";";
+            final String metaClassFieldSignature = Type.getDescriptor(MetaClass.class);
             visitor.visitField(Opcodes.ACC_PRIVATE, "metaClass", metaClassFieldSignature, null, null);
 
             initMetaClass = new MethodCodeBody() {
@@ -598,6 +636,9 @@ public class AsmBackedClassGenerator extends AbstractClassGenerator {
         }
 
         public void addGetter(MetaBeanProperty property) throws Exception {
+            if (!extensible) {
+                return;
+            }
             MetaMethod getter = property.getGetter();
 
             // GENERATE private boolean <prop>Set;
@@ -606,7 +647,7 @@ public class AsmBackedClassGenerator extends AbstractClassGenerator {
             visitor.visitField(Opcodes.ACC_PRIVATE, flagName, Type.BOOLEAN_TYPE.getDescriptor(), null, null);
 
             addConventionGetter(getter.getName(), flagName, property);
-    
+
             String getterName = getter.getName();
             Class<?> returnType = getter.getReturnType();
 
@@ -681,6 +722,9 @@ public class AsmBackedClassGenerator extends AbstractClassGenerator {
         }
 
         public void addSetter(MetaBeanProperty property) throws Exception {
+            if (!extensible) {
+                return;
+            }
             MetaMethod setter = property.getSetter();
 
             // GENERATE public <return-type> <setter>(<type> v) { <return-type> v = super.<setter>(v); <prop>Set = true; return v; }
@@ -740,6 +784,9 @@ public class AsmBackedClassGenerator extends AbstractClassGenerator {
         }
 
         public void overrideSetMethod(MetaBeanProperty property, MetaMethod metaMethod) throws Exception {
+            if (!extensible) {
+                return;
+            }
             Type paramType = Type.getType(metaMethod.getParameterTypes()[0].getTheClass());
             Type returnType = Type.getType(metaMethod.getReturnType());
             String methodDescriptor = Type.getMethodDescriptor(returnType, new Type[]{paramType});
diff --git a/subprojects/core/src/main/groovy/org/gradle/api/internal/BeanDynamicObject.java b/subprojects/core/src/main/groovy/org/gradle/api/internal/BeanDynamicObject.java
index 01b2bc3..53e593d 100644
--- a/subprojects/core/src/main/groovy/org/gradle/api/internal/BeanDynamicObject.java
+++ b/subprojects/core/src/main/groovy/org/gradle/api/internal/BeanDynamicObject.java
@@ -17,8 +17,15 @@ package org.gradle.api.internal;
 
 import groovy.lang.*;
 import groovy.lang.MissingMethodException;
+import org.codehaus.groovy.reflection.ParameterTypes;
 import org.codehaus.groovy.runtime.InvokerInvocationException;
+import org.gradle.api.internal.coerce.MethodArgumentsTransformer;
+import org.gradle.api.internal.coerce.TypeCoercingMethodArgumentsTransformer;
+import org.gradle.api.specs.Spec;
+import org.gradle.internal.reflect.JavaReflectionUtil;
 
+import java.lang.reflect.Method;
+import java.lang.reflect.Modifier;
 import java.util.Collections;
 import java.util.HashMap;
 import java.util.List;
@@ -28,12 +35,15 @@ import java.util.Map;
  * A {@link DynamicObject} which uses groovy reflection to provide access to the properties and methods of a bean.
  */
 public class BeanDynamicObject extends AbstractDynamicObject {
-    
+
     private final Object bean;
     private final boolean includeProperties;
     private final DynamicObject delegate;
     private final boolean implementsMissing;
 
+    // NOTE: If this guy starts caching internally, consider sharing an instance
+    private final MethodArgumentsTransformer argsTransformer = new TypeCoercingMethodArgumentsTransformer();
+
     public BeanDynamicObject(Object bean) {
         this(bean, true);
     }
@@ -56,7 +66,7 @@ public class BeanDynamicObject extends AbstractDynamicObject {
             return new GroovyObjectAdapter();
         }
     }
-    
+
     public BeanDynamicObject withNoProperties() {
         return new BeanDynamicObject(bean, false);
     }
@@ -94,7 +104,7 @@ public class BeanDynamicObject extends AbstractDynamicObject {
 
     @Override
     public boolean hasProperty(String name) {
-        return delegate.hasProperty(name);    
+        return delegate.hasProperty(name);
     }
 
     @Override
@@ -104,22 +114,24 @@ public class BeanDynamicObject extends AbstractDynamicObject {
 
     @Override
     public void setProperty(final String name, Object value) throws MissingPropertyException {
-        delegate.setProperty(name, value); 
+        delegate.setProperty(name, value);
     }
 
     @Override
     public Map<String, ?> getProperties() {
-        return delegate.getProperties();        
+        return delegate.getProperties();
     }
 
     @Override
     public boolean hasMethod(String name, Object... arguments) {
-        return delegate.hasMethod(name, arguments);                        
+        return delegate.hasMethod(name, arguments);
     }
 
     @Override
     public Object invokeMethod(String name, Object... arguments) throws MissingMethodException {
-        return delegate.invokeMethod(name, arguments);        
+        // Maybe transform the arguments before calling the method (e.g. type coercion)
+        arguments = argsTransformer.transform(bean, name, arguments);
+        return delegate.invokeMethod(name, arguments);
     }
 
     private class MetaClassAdapter implements DynamicObject {
@@ -160,9 +172,7 @@ public class BeanDynamicObject extends AbstractDynamicObject {
             MetaClass metaClass = getMetaClass();
             MetaProperty property = metaClass.hasProperty(bean, name);
             if (property == null) {
-                if (property == null) {
-                    getMetaClass().invokeMissingProperty(bean, name, null, false);
-                }
+                getMetaClass().invokeMissingProperty(bean, name, null, false);
             }
 
             if (property instanceof MetaBeanProperty && ((MetaBeanProperty) property).getSetter() == null) {
@@ -175,6 +185,10 @@ public class BeanDynamicObject extends AbstractDynamicObject {
                 };
             }
             try {
+
+                // Attempt type coercion before trying to set the property
+                value = argsTransformer.transform(bean, MetaProperty.getSetterName(name), value)[0];
+
                 metaClass.setProperty(bean, name, value);
             } catch (InvokerInvocationException e) {
                 if (e.getCause() instanceof RuntimeException) {
@@ -207,11 +221,27 @@ public class BeanDynamicObject extends AbstractDynamicObject {
             return properties;
         }
 
-        public boolean hasMethod(String name, Object... arguments) {
-            return !getMetaClass().respondsTo(bean, name, arguments).isEmpty();
+        public boolean hasMethod(final String name, final Object... arguments) {
+            boolean respondsTo = !getMetaClass().respondsTo(bean, name, arguments).isEmpty();
+            if (respondsTo) {
+                return true;
+            } else {
+                Method method = JavaReflectionUtil.findMethod(bean.getClass(), new Spec<Method>() {
+                    public boolean isSatisfiedBy(Method potentialMethod) {
+                        if (Modifier.isPrivate(potentialMethod.getModifiers()) && potentialMethod.getName().equals(name)) {
+                            ParameterTypes parameterTypes = new ParameterTypes(potentialMethod.getParameterTypes());
+                            return parameterTypes.isValidMethod(arguments);
+                        } else {
+                            return false;
+                        }
+                    }
+                });
+
+                return method != null;
+            }
         }
 
-        public Object invokeMethod(String name, Object... arguments) throws MissingMethodException {
+        public Object invokeMethod(final String name, final Object... arguments) throws MissingMethodException {
             try {
                 return getMetaClass().invokeMethod(bean, name, arguments);
             } catch (InvokerInvocationException e) {
@@ -219,8 +249,6 @@ public class BeanDynamicObject extends AbstractDynamicObject {
                     throw (RuntimeException) e.getCause();
                 }
                 throw e;
-            } catch (MissingMethodException e) {
-                throw methodMissingException(name, arguments);
             }
         }
 
@@ -242,7 +270,7 @@ public class BeanDynamicObject extends AbstractDynamicObject {
        So in this case we use these methods directly on the GroovyObject in case it does implement logic at this level.
      */
     private class GroovyObjectAdapter extends MetaClassAdapter {
-        private final GroovyObject groovyObject = (GroovyObject)bean;
+        private final GroovyObject groovyObject = (GroovyObject) bean;
 
 
         @Override
diff --git a/subprojects/core/src/main/groovy/org/gradle/api/internal/CachingDirectedGraphWalker.java b/subprojects/core/src/main/groovy/org/gradle/api/internal/CachingDirectedGraphWalker.java
deleted file mode 100644
index 379a79b..0000000
--- a/subprojects/core/src/main/groovy/org/gradle/api/internal/CachingDirectedGraphWalker.java
+++ /dev/null
@@ -1,176 +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;
-
-import org.gradle.util.GUtil;
-
-import java.util.*;
-
-/**
- * A graph walker which collects the values reachable from a given set of start nodes. Handles cycles in the graph. Can
- * be reused to perform multiple searches, and reuses the results of previous searches.
- *
- * Uses a variation of Tarjan's algorithm: http://en.wikipedia.org/wiki/Tarjan%27s_strongly_connected_components_algorithm
- */
-public class CachingDirectedGraphWalker<N, T> {
-    private final DirectedGraphWithEdgeValues<N, T> graph;
-    private List<N> startNodes = new LinkedList<N>();
-    private final Map<N, Set<T>> cachedNodeValues = new HashMap<N, Set<T>>();
-
-    public CachingDirectedGraphWalker(DirectedGraph<N, T> graph) {
-        this.graph = new GraphWithEmpyEdges<N, T>(graph);
-    }
-
-    public CachingDirectedGraphWalker(DirectedGraphWithEdgeValues<N, T> graph) {
-        this.graph = graph;
-    }
-
-    /**
-     * Adds some start nodes.
-     */
-    public CachingDirectedGraphWalker<N, T> add(N... values) {
-        add(Arrays.asList(values));
-        return this;
-    }
-
-    /**
-     * Adds some start nodes.
-     */
-    public CachingDirectedGraphWalker add(Iterable<? extends N> values) {
-        GUtil.addToCollection(startNodes, values);
-        return this;
-    }
-
-    /**
-     * Calculates the set of values of nodes reachable from the start nodes.
-     */
-    public Set<T> findValues() {
-        try {
-            return doSearch();
-        } finally {
-            startNodes.clear();
-        }
-    }
-
-    private Set<T> doSearch() {
-        int componentCount = 0;
-        Map<N, NodeDetails<N, T>> seenNodes = new HashMap<N, NodeDetails<N, T>>();
-        Map<Integer, NodeDetails<N, T>> components = new HashMap<Integer, NodeDetails<N, T>>();
-        LinkedList<N> queue = new LinkedList<N>(startNodes);
-
-        while (!queue.isEmpty()) {
-            N node = queue.getFirst();
-            NodeDetails<N, T> details = seenNodes.get(node);
-            if (details == null) {
-                // Have not visited this node yet. Push its successors onto the queue in front of this node and visit
-                // them
-
-                details = new NodeDetails<N, T>(node, componentCount++);
-                seenNodes.put(node, details);
-                components.put(details.component, details);
-
-                Set<T> cacheValues = cachedNodeValues.get(node);
-                if (cacheValues != null) {
-                    // Already visited this node
-                    details.values = cacheValues;
-                    details.finished = true;
-                    queue.removeFirst();
-                    continue;
-                }
-
-                graph.getNodeValues(node, details.values, details.successors);
-                for (N connectedNode : details.successors) {
-                    if (!seenNodes.containsKey(connectedNode)) {
-                        queue.add(0, connectedNode);
-                    }
-                    // Else, already visiting or have visited the successor node (we're in a cycle)
-                }
-            } else {
-                // Have visited all of this node's successors
-                queue.removeFirst();
-
-                if (cachedNodeValues.containsKey(node)) {
-                    continue;
-                }
-
-                for (N connectedNode : details.successors) {
-                    NodeDetails<N, T> connectedNodeDetails = seenNodes.get(connectedNode);
-                    if (!connectedNodeDetails.finished) {
-                        // part of a cycle
-                        details.minSeen = Math.min(details.minSeen, connectedNodeDetails.minSeen);
-                    }
-                    details.values.addAll(connectedNodeDetails.values);
-                    graph.getEdgeValues(node, connectedNode, details.values);
-                }
-
-                if (details.minSeen != details.component) {
-                    // Part of a strongly connected component (ie cycle) - move values to root of the component
-                    // The root is the first node of the component we encountered
-                    NodeDetails<N, T> rootDetails = components.get(details.minSeen);
-                    rootDetails.values.addAll(details.values);
-                    details.values.clear();
-                    rootDetails.strongComponentMembers.addAll(details.strongComponentMembers);
-                } else {
-                    // Not part of a strongly connected component or the root of a strongly connected component
-                    for (NodeDetails<N, T> componentMember : details.strongComponentMembers) {
-                        cachedNodeValues.put(componentMember.node, details.values);
-                        componentMember.finished = true;
-                        components.remove(componentMember.component);
-                    }
-                }
-            }
-        }
-
-        Set<T> values = new LinkedHashSet<T>();
-        for (N startNode : startNodes) {
-            values.addAll(cachedNodeValues.get(startNode));
-        }
-        return values;
-    }
-
-    private static class NodeDetails<N, T> {
-        private final int component;
-        private final N node;
-        private Set<T> values = new LinkedHashSet<T>();
-        private List<N> successors = new ArrayList<N>();
-        private Set<NodeDetails<N, T>> strongComponentMembers = new LinkedHashSet<NodeDetails<N, T>>();
-        private int minSeen;
-        private boolean finished;
-
-        public NodeDetails(N node, int component) {
-            this.node = node;
-            this.component = component;
-            minSeen = component;
-            strongComponentMembers.add(this);
-        }
-    }
-
-    private static class GraphWithEmpyEdges<N, T> implements DirectedGraphWithEdgeValues<N, T> {
-        private final DirectedGraph<N, T> graph;
-
-        public GraphWithEmpyEdges(DirectedGraph<N, T> graph) {
-            this.graph = graph;
-        }
-
-        public void getEdgeValues(N from, N to, Collection<T> values) {
-        }
-
-        public void getNodeValues(N node, Collection<T> values, Collection<N> connectedNodes) {
-            graph.getNodeValues(node, values, connectedNodes);
-        }
-    }
-}
diff --git a/subprojects/core/src/main/groovy/org/gradle/api/internal/ClassGeneratorBackedInstantiator.java b/subprojects/core/src/main/groovy/org/gradle/api/internal/ClassGeneratorBackedInstantiator.java
index 2a28305..4ac370e 100644
--- a/subprojects/core/src/main/groovy/org/gradle/api/internal/ClassGeneratorBackedInstantiator.java
+++ b/subprojects/core/src/main/groovy/org/gradle/api/internal/ClassGeneratorBackedInstantiator.java
@@ -26,7 +26,7 @@ public class ClassGeneratorBackedInstantiator implements Instantiator {
         this.instantiator = instantiator;
     }
 
-    public <T> T newInstance(Class<T> type, Object... parameters) {
+    public <T> T newInstance(Class<? extends T> type, Object... parameters) {
         // During the construction of the object, it will look for this global instantiator.
         // This is to support ExtensionContainer.add(String, Class, Object...) which facilitates
         // making extensions ExtensionAware themselves.
diff --git a/subprojects/core/src/main/groovy/org/gradle/api/internal/CompositeDomainObjectSet.java b/subprojects/core/src/main/groovy/org/gradle/api/internal/CompositeDomainObjectSet.java
index 9e8f4de..dadd3f1 100644
--- a/subprojects/core/src/main/groovy/org/gradle/api/internal/CompositeDomainObjectSet.java
+++ b/subprojects/core/src/main/groovy/org/gradle/api/internal/CompositeDomainObjectSet.java
@@ -81,9 +81,11 @@ public class CompositeDomainObjectSet<T> extends DefaultDomainObjectSet<T> {
     }
     
     public void addCollection(DomainObjectCollection<? extends T> collection) {
-        getStore().addComposited(collection);
-        collection.all(getEventRegister().getAddAction());
-        collection.whenObjectRemoved(getEventRegister().getRemoveAction());
+        if (!getStore().getCollections().contains(collection)) {
+            getStore().addComposited(collection);
+            collection.all(getEventRegister().getAddAction());
+            collection.whenObjectRemoved(getEventRegister().getRemoveAction());
+        }
     }
 
     public void removeCollection(DomainObjectCollection<? extends T> collection) {
diff --git a/subprojects/core/src/main/groovy/org/gradle/api/internal/ConventionAwareHelper.java b/subprojects/core/src/main/groovy/org/gradle/api/internal/ConventionAwareHelper.java
index 05e5925..b80f13d 100644
--- a/subprojects/core/src/main/groovy/org/gradle/api/internal/ConventionAwareHelper.java
+++ b/subprojects/core/src/main/groovy/org/gradle/api/internal/ConventionAwareHelper.java
@@ -22,20 +22,17 @@ import org.gradle.api.InvalidUserDataException;
 import org.gradle.api.internal.plugins.DefaultConvention;
 import org.gradle.api.plugins.Convention;
 import org.gradle.internal.UncheckedException;
-import org.gradle.util.ReflectionUtil;
+import org.gradle.internal.reflect.JavaReflectionUtil;
 
 import java.util.Collection;
 import java.util.HashMap;
 import java.util.Map;
 import java.util.concurrent.Callable;
 
-/**
- * @author Hans Dockter
- */
 public class ConventionAwareHelper implements ConventionMapping, HasConvention {
     //prefix internal fields with _ so that they don't get into the way of propertyMissing()
     private final Convention _convention;
-    private IConventionAware _source;
+    private final IConventionAware _source;
     private final Map<String, MappedPropertyImpl> _mappings = new HashMap<String, MappedPropertyImpl>();
 
     /**
@@ -55,7 +52,7 @@ public class ConventionAwareHelper implements ConventionMapping, HasConvention {
     }
 
     private MappedProperty map(String propertyName, Value<?> value) {
-        if (!ReflectionUtil.hasProperty(_source, propertyName)) {
+        if (!JavaReflectionUtil.propertyExists(_source, propertyName)) {
             throw new InvalidUserDataException(
                     "You can't map a property that does not exist: propertyName=" + propertyName);
         }
@@ -124,14 +121,6 @@ public class ConventionAwareHelper implements ConventionMapping, HasConvention {
         return _convention;
     }
 
-    public IConventionAware getSource() {
-        return _source;
-    }
-
-    public void setSource(IConventionAware source) {
-        this._source = source;
-    }
-
     private static class MappedPropertyImpl implements MappedProperty {
         private final Value<?> value;
         private boolean haveValue;
diff --git a/subprojects/core/src/main/groovy/org/gradle/api/internal/ConventionTask.java b/subprojects/core/src/main/groovy/org/gradle/api/internal/ConventionTask.java
index f79245a..c5e36de 100644
--- a/subprojects/core/src/main/groovy/org/gradle/api/internal/ConventionTask.java
+++ b/subprojects/core/src/main/groovy/org/gradle/api/internal/ConventionTask.java
@@ -22,9 +22,6 @@ import org.gradle.api.Task;
 
 import java.util.concurrent.Callable;
 
-/**
- * @author Hans Dockter
- */
 public abstract class ConventionTask extends DefaultTask implements IConventionAware {
     private ConventionMapping conventionMapping;
 
diff --git a/subprojects/core/src/main/groovy/org/gradle/api/internal/DefaultNamedDomainObjectCollection.java b/subprojects/core/src/main/groovy/org/gradle/api/internal/DefaultNamedDomainObjectCollection.java
index 5d6dfbc..77a7b2b 100644
--- a/subprojects/core/src/main/groovy/org/gradle/api/internal/DefaultNamedDomainObjectCollection.java
+++ b/subprojects/core/src/main/groovy/org/gradle/api/internal/DefaultNamedDomainObjectCollection.java
@@ -130,6 +130,14 @@ public class DefaultNamedDomainObjectCollection<T> extends DefaultDomainObjectCo
         return map;
     }
 
+    public SortedSet<String> getNames() {
+        SortedSet<String> set = new TreeSet<String>();
+        for (T o : getStore()) {
+            set.add(namer.determineName(o));
+        }
+        return set;
+    }
+
     public <S extends T> NamedDomainObjectCollection<S> withType(Class<S> type) {
         return filtered(createFilter(type));
     }
diff --git a/subprojects/core/src/main/groovy/org/gradle/api/internal/DefaultPolymorphicDomainObjectContainer.java b/subprojects/core/src/main/groovy/org/gradle/api/internal/DefaultPolymorphicDomainObjectContainer.java
index 06e2635..5d7ddaa 100644
--- a/subprojects/core/src/main/groovy/org/gradle/api/internal/DefaultPolymorphicDomainObjectContainer.java
+++ b/subprojects/core/src/main/groovy/org/gradle/api/internal/DefaultPolymorphicDomainObjectContainer.java
@@ -15,10 +15,15 @@
  */
 package org.gradle.api.internal;
 
+import com.google.common.base.Joiner;
+import com.google.common.collect.Lists;
+import groovy.lang.Closure;
 import org.gradle.api.*;
 import org.gradle.internal.reflect.Instantiator;
 
+import java.util.Collections;
 import java.util.HashMap;
+import java.util.List;
 import java.util.Map;
 
 public class DefaultPolymorphicDomainObjectContainer<T> extends AbstractPolymorphicDomainObjectContainer<T>
@@ -39,8 +44,9 @@ public class DefaultPolymorphicDomainObjectContainer<T> extends AbstractPolymorp
 
     protected T doCreate(String name) {
         if (defaultFactory == null) {
-            throw new InvalidUserDataException("This container does not support "
-                    + "creating domain objects without specifying a type.");
+            throw new InvalidUserDataException(String.format("Cannot create a %s named '%s' because this container "
+                    + "does not support creating elements by name alone. Please specify which subtype of %s to create. "
+                    + "Known subtypes are: %s", getTypeDisplayName(), name, getTypeDisplayName(), getSupportedTypeNames()));
         }
         return defaultFactory.create(name);
     }
@@ -49,8 +55,8 @@ public class DefaultPolymorphicDomainObjectContainer<T> extends AbstractPolymorp
         @SuppressWarnings("unchecked")
         NamedDomainObjectFactory<U> factory = (NamedDomainObjectFactory<U>) factories.get(type);
         if (factory == null) {
-            throw new InvalidUserDataException(String.format("This container does not support "
-                    + "creating domain objects of type '%s'.", type.getName()));
+            throw new InvalidUserDataException(String.format("Cannot create a %s because this type is not known "
+                    + "to this container. Known types are: %s", type.getSimpleName(), getSupportedTypeNames()));
         }
         return factory.create(name);
     }
@@ -61,9 +67,36 @@ public class DefaultPolymorphicDomainObjectContainer<T> extends AbstractPolymorp
 
     public <U extends T> void registerFactory(Class<U> type, NamedDomainObjectFactory<? extends U> factory) {
         if (!getType().isAssignableFrom(type)) {
-            throw new IllegalArgumentException(String.format("Factory element type '%s' is not a subtype of "
-                    + "container element type '%s'", type.getName(), getType().getName()));
+            throw new IllegalArgumentException(String.format("Cannot register a factory for type %s because "
+                    + "it is not a subtype of container element type %s.", type.getSimpleName(), getTypeDisplayName()));
         }
         factories.put(type, factory);
     }
+
+    public <U extends T> void registerFactory(Class<U> type, final Closure<? extends U> factory) {
+        registerFactory(type, new NamedDomainObjectFactory<U>() {
+            public U create(String name) {
+                return factory.call(name);
+            }
+        });
+    }
+
+    public <U extends T> void registerBinding(Class<U> type, final Class<? extends U> implementationType) {
+        registerFactory(type, new NamedDomainObjectFactory<U>() {
+            boolean named = Named.class.isAssignableFrom(implementationType);
+            public U create(String name) {
+                return named ? getInstantiator().newInstance(implementationType, name)
+                        : getInstantiator().newInstance(implementationType);
+            }
+        });
+    }
+
+    private String getSupportedTypeNames() {
+        List<String> names = Lists.newArrayList();
+        for (Class<?> clazz : factories.keySet()) {
+            names.add(clazz.getSimpleName());
+        }
+        Collections.sort(names);
+        return names.isEmpty() ? "(None)" : Joiner.on(", ").join(names);
+    }
 }
diff --git a/subprojects/core/src/main/groovy/org/gradle/api/internal/DependencyInjectingInstantiator.java b/subprojects/core/src/main/groovy/org/gradle/api/internal/DependencyInjectingInstantiator.java
index 168b46f..2d220b9 100644
--- a/subprojects/core/src/main/groovy/org/gradle/api/internal/DependencyInjectingInstantiator.java
+++ b/subprojects/core/src/main/groovy/org/gradle/api/internal/DependencyInjectingInstantiator.java
@@ -52,7 +52,7 @@ public class DependencyInjectingInstantiator implements Instantiator {
         this.onDeprecationWarning = onDeprecationWarning;
     }
 
-    public <T> T newInstance(Class<T> type, Object... parameters) {
+    public <T> T newInstance(Class<? extends T> type, Object... parameters) {
         try {
             validateType(type);
             Constructor<?> constructor = selectConstructor(type, parameters);
diff --git a/subprojects/core/src/main/groovy/org/gradle/api/internal/DirectedGraph.java b/subprojects/core/src/main/groovy/org/gradle/api/internal/DirectedGraph.java
deleted file mode 100644
index 930dd5c..0000000
--- a/subprojects/core/src/main/groovy/org/gradle/api/internal/DirectedGraph.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;
-
-import java.util.Collection;
-
-/**
- * A directed graph with nodes of type N. Each node has a collection of values of type V.
- */
-public interface DirectedGraph<N, V> {
-    void getNodeValues(N node, Collection<V> values, Collection<N> connectedNodes);
-}
diff --git a/subprojects/core/src/main/groovy/org/gradle/api/internal/DirectedGraphWithEdgeValues.java b/subprojects/core/src/main/groovy/org/gradle/api/internal/DirectedGraphWithEdgeValues.java
deleted file mode 100644
index 16a5d0d..0000000
--- a/subprojects/core/src/main/groovy/org/gradle/api/internal/DirectedGraphWithEdgeValues.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.internal;
-
-import java.util.Collection;
-
-/**
- * A directed graph with nodes of type N. Each edge has a collection of values of type V
- */
-public interface DirectedGraphWithEdgeValues<N, V> extends DirectedGraph<N, V> {
-    void getEdgeValues(N from, N to, Collection<V> values);
-}
diff --git a/subprojects/core/src/main/groovy/org/gradle/api/internal/DocumentationRegistry.java b/subprojects/core/src/main/groovy/org/gradle/api/internal/DocumentationRegistry.java
index ba7d428..e7182e1 100644
--- a/subprojects/core/src/main/groovy/org/gradle/api/internal/DocumentationRegistry.java
+++ b/subprojects/core/src/main/groovy/org/gradle/api/internal/DocumentationRegistry.java
@@ -18,45 +18,20 @@ package org.gradle.api.internal;
 
 import org.gradle.util.GradleVersion;
 
-import java.io.File;
-
 /**
  * Locates documentation for various features.
  */
 public class DocumentationRegistry {
-    private final GradleDistributionLocator locator;
     private final GradleVersion gradleVersion;
 
-    public DocumentationRegistry(GradleDistributionLocator locator) {
-        this(locator, GradleVersion.current());
-    }
-
-    public DocumentationRegistry(GradleDistributionLocator locator, GradleVersion gradleVersion) {
-        this.locator = locator;
-        this.gradleVersion = gradleVersion;
+    public DocumentationRegistry() {
+        this.gradleVersion = GradleVersion.current();
     }
 
     /**
      * Returns the location the documentation for the given feature, referenced by id. The location may be local or remote.
      */
     public String getDocumentationFor(String id) {
-        if (locator.getGradleHome() != null) {
-            File pageLocation = new File(locator.getGradleHome(), String.format("docs/userguide/%s.html", id));
-            File userGuideLocation = new File(locator.getGradleHome(), "docs/userguide/userguide.html");
-            if (pageLocation.isFile() && userGuideLocation.isFile()) {
-                return pageLocation.getAbsolutePath();
-            }
-            if (!pageLocation.isFile() && userGuideLocation.isFile()) {
-                throw new IllegalArgumentException(String.format("User guide page '%s' not found.", pageLocation));
-            }
-            if (pageLocation.isFile() && !userGuideLocation.isFile()) {
-                throw new IllegalArgumentException(String.format("User guide page '%s' not found.", userGuideLocation));
-            }
-        }
         return String.format("http://gradle.org/docs/%s/userguide/%s.html", gradleVersion.getVersion(), id);
     }
-
-    public String getFeatureLifecycle() {
-        return getDocumentationFor("feature_lifecycle");
-    }
 }
diff --git a/subprojects/core/src/main/groovy/org/gradle/api/internal/GradleInternal.java b/subprojects/core/src/main/groovy/org/gradle/api/internal/GradleInternal.java
index fbc439d..b840a1d 100644
--- a/subprojects/core/src/main/groovy/org/gradle/api/internal/GradleInternal.java
+++ b/subprojects/core/src/main/groovy/org/gradle/api/internal/GradleInternal.java
@@ -15,12 +15,13 @@
  */
 package org.gradle.api.internal;
 
+import org.gradle.BuildListener;
 import org.gradle.api.ProjectEvaluationListener;
-import org.gradle.api.internal.project.*;
+import org.gradle.api.internal.project.ProjectInternal;
+import org.gradle.api.internal.project.ProjectRegistry;
+import org.gradle.internal.service.scopes.ServiceRegistryFactory;
 import org.gradle.api.invocation.Gradle;
 import org.gradle.execution.TaskGraphExecuter;
-import org.gradle.BuildListener;
-import org.gradle.util.MultiParentClassLoader;
 
 /**
  * An internal interface for Gradle that exposed objects and concepts that are not intended for public
@@ -42,12 +43,7 @@ public interface GradleInternal extends Gradle {
      */
     ProjectInternal getDefaultProject();
 
-    IProjectRegistry<ProjectInternal> getProjectRegistry();
-
-    /**
-     * Returns the root {@code ClassLoader} to use for the scripts of this build.
-     */
-    MultiParentClassLoader getScriptClassLoader();
+    ProjectRegistry<ProjectInternal> getProjectRegistry();
 
     /**
      * Returns the broadcaster for {@link ProjectEvaluationListener} events for this build
diff --git a/subprojects/core/src/main/groovy/org/gradle/api/internal/GraphAggregator.java b/subprojects/core/src/main/groovy/org/gradle/api/internal/GraphAggregator.java
deleted file mode 100644
index 68d5c2c..0000000
--- a/subprojects/core/src/main/groovy/org/gradle/api/internal/GraphAggregator.java
+++ /dev/null
@@ -1,90 +0,0 @@
-/*
- * Copyright 2010 the original author or authors.
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- *      http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-package org.gradle.api.internal;
-
-import java.util.*;
-
-/**
- * Groups the nodes of a graph based on their reachability from a set of starting nodes.
- */
-public class GraphAggregator<N> {
-    private final CachingDirectedGraphWalker<N, N> graphWalker;
-
-    public GraphAggregator(DirectedGraph<N, ?> graph) {
-        graphWalker = new CachingDirectedGraphWalker<N, N>(new ConnectedNodesAsValuesDirectedGraph<N>(graph));
-    }
-
-    public Result<N> group(Collection<? extends N> startNodes, Collection<? extends N> allNodes) {
-        Map<N, Set<N>> reachableByNode = new HashMap<N, Set<N>>();
-        Set<N> topLevelNodes = new LinkedHashSet<N>(allNodes);
-        for (N node : allNodes) {
-            Set<N> reachableNodes = graphWalker.add(node).findValues();
-            reachableByNode.put(node, reachableNodes);
-            topLevelNodes.removeAll(reachableNodes);
-        }
-        topLevelNodes.addAll(startNodes);
-        Map<N, Set<N>> nodes = new HashMap<N, Set<N>>();
-        for (N node : topLevelNodes) {
-            nodes.put(node, calculateReachableNodes(reachableByNode, node, topLevelNodes));
-        }
-        return new Result<N>(nodes, topLevelNodes);
-    }
-
-    private Set<N> calculateReachableNodes(Map<N, Set<N>> nodes, N node, Set<N> topLevelNodes) {
-        Set<N> reachableNodes = nodes.get(node);
-        reachableNodes.add(node);
-        Set<N> reachableStartNodes = new LinkedHashSet<N>(topLevelNodes);
-        reachableStartNodes.retainAll(reachableNodes);
-        reachableStartNodes.remove(node);
-        for (N startNode : reachableStartNodes) {
-            reachableNodes.removeAll(calculateReachableNodes(nodes, startNode, topLevelNodes));
-        }
-        return reachableNodes;
-    }
-
-    public static class Result<N> {
-        private final Map<N, Set<N>> nodes;
-        private final Set<N> topLevelNodes;
-
-        public Result(Map<N, Set<N>> nodes, Set<N> topLevelNodes) {
-            this.nodes = nodes;
-            this.topLevelNodes = topLevelNodes;
-        }
-
-        public Set<N> getNodes(N startNode) {
-            return nodes.get(startNode);
-        }
-
-        public Set<N> getTopLevelNodes() {
-            return topLevelNodes;
-        }
-    }
-
-    private static class ConnectedNodesAsValuesDirectedGraph<N> implements DirectedGraph<N, N> {
-        private final DirectedGraph<N, ?> graph;
-
-        private ConnectedNodesAsValuesDirectedGraph(DirectedGraph<N, ?> graph) {
-            this.graph = graph;
-        }
-
-        public void getNodeValues(N node, Collection<N> values, Collection<N> connectedNodes) {
-            Set<N> edges = new LinkedHashSet<N>();
-            graph.getNodeValues(node, new ArrayList(), edges);
-            values.addAll(edges);
-            connectedNodes.addAll(edges);
-        }
-    }
-}
diff --git a/subprojects/core/src/main/groovy/org/gradle/api/internal/IConventionAware.java b/subprojects/core/src/main/groovy/org/gradle/api/internal/IConventionAware.java
index a4139a7..eeb4553 100644
--- a/subprojects/core/src/main/groovy/org/gradle/api/internal/IConventionAware.java
+++ b/subprojects/core/src/main/groovy/org/gradle/api/internal/IConventionAware.java
@@ -22,8 +22,6 @@ package org.gradle.api.internal;
  *
  * <p>Each getter of an {@code IConventionAware} object should use the mappings to determine the value for the property,
  * when no value has been explicitly set for the property.</p>
- *
- * @author Hans Dockter
  */
 public interface IConventionAware {
 
diff --git a/subprojects/core/src/main/groovy/org/gradle/api/internal/NamedDomainObjectContainerConfigureDelegate.java b/subprojects/core/src/main/groovy/org/gradle/api/internal/NamedDomainObjectContainerConfigureDelegate.java
index ce26dc4..c6c0a05 100644
--- a/subprojects/core/src/main/groovy/org/gradle/api/internal/NamedDomainObjectContainerConfigureDelegate.java
+++ b/subprojects/core/src/main/groovy/org/gradle/api/internal/NamedDomainObjectContainerConfigureDelegate.java
@@ -18,15 +18,15 @@ package org.gradle.api.internal;
 import org.gradle.api.NamedDomainObjectContainer;
 
 public class NamedDomainObjectContainerConfigureDelegate extends ConfigureDelegate {
-    private final NamedDomainObjectContainer container;
+    private final NamedDomainObjectContainer _container;
 
     public NamedDomainObjectContainerConfigureDelegate(Object owner, NamedDomainObjectContainer container) {
         super(owner, container);
-        this.container = container;
+        _container = container;
     }
 
     @Override
     protected void _configure(String name, Object[] params) {
-        container.create(name);
+        _container.create(name);
     }
 }
\ No newline at end of file
diff --git a/subprojects/core/src/main/groovy/org/gradle/api/internal/PolymorphicDomainObjectContainerConfigureDelegate.java b/subprojects/core/src/main/groovy/org/gradle/api/internal/PolymorphicDomainObjectContainerConfigureDelegate.java
index f07befc..81bb9e1 100644
--- a/subprojects/core/src/main/groovy/org/gradle/api/internal/PolymorphicDomainObjectContainerConfigureDelegate.java
+++ b/subprojects/core/src/main/groovy/org/gradle/api/internal/PolymorphicDomainObjectContainerConfigureDelegate.java
@@ -20,25 +20,27 @@ import org.gradle.api.PolymorphicDomainObjectContainer;
 import groovy.lang.Closure;
 
 public class PolymorphicDomainObjectContainerConfigureDelegate extends NamedDomainObjectContainerConfigureDelegate {
-    private final PolymorphicDomainObjectContainer container;
+    private final PolymorphicDomainObjectContainer _container;
 
     public PolymorphicDomainObjectContainerConfigureDelegate(Object owner, PolymorphicDomainObjectContainer container) {
         super(owner, container);
-        this.container = container;
+        this._container = container;
     }
 
     @Override
     protected boolean _isConfigureMethod(String name, Object[] params) {
-        return super._isConfigureMethod(name, params) || params.length == 2 && params[0] instanceof Class && params[1] instanceof Closure;
+        return super._isConfigureMethod(name, params)
+                || params.length == 1 && params[0] instanceof Class
+                || params.length == 2 && params[0] instanceof Class && params[1] instanceof Closure;
     }
 
     @Override
     @SuppressWarnings("unchecked")
     protected void _configure(String name, Object[] params) {
-        if (params.length <= 1) {
-            container.create(name);
+        if (params.length > 0 && params[0] instanceof Class) {
+            _container.create(name, (Class) params[0]);
         } else {
-            container.create(name, (Class) params[0]);
+            _container.create(name);
         }
     }
 }
\ No newline at end of file
diff --git a/subprojects/core/src/main/groovy/org/gradle/api/internal/SettingsInternal.java b/subprojects/core/src/main/groovy/org/gradle/api/internal/SettingsInternal.java
index 04575f4..969d18e 100644
--- a/subprojects/core/src/main/groovy/org/gradle/api/internal/SettingsInternal.java
+++ b/subprojects/core/src/main/groovy/org/gradle/api/internal/SettingsInternal.java
@@ -18,7 +18,7 @@ package org.gradle.api.internal;
 
 import org.gradle.StartParameter;
 import org.gradle.api.initialization.Settings;
-import org.gradle.api.internal.project.IProjectRegistry;
+import org.gradle.api.internal.project.ProjectRegistry;
 import org.gradle.groovy.scripts.ScriptSource;
 import org.gradle.initialization.DefaultProjectDescriptor;
 
@@ -29,5 +29,5 @@ public interface SettingsInternal extends Settings {
 
     ScriptSource getSettingsScript();
 
-    IProjectRegistry<DefaultProjectDescriptor> getProjectRegistry();
+    ProjectRegistry<DefaultProjectDescriptor> getProjectRegistry();
 }
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 d062bf4..37eaaa0 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
@@ -17,6 +17,7 @@
 package org.gradle.api.internal;
 
 import org.gradle.api.Task;
+import org.gradle.api.internal.tasks.ContextAwareTaskAction;
 import org.gradle.api.internal.tasks.TaskExecuter;
 import org.gradle.api.internal.tasks.TaskStateInternal;
 import org.gradle.api.internal.tasks.execution.TaskValidator;
@@ -25,10 +26,15 @@ import org.gradle.internal.Factory;
 import org.gradle.logging.StandardOutputCapture;
 import org.gradle.util.Configurable;
 
-import java.util.List;
 import java.io.File;
+import java.util.List;
 
 public interface TaskInternal extends Task, Configurable<Task> {
+
+    // Can we just override Task.getActions()?
+    // Would need to change return type on Task API to: List<? super Action<? super Task>> : not certain this is back-compatible
+    List<ContextAwareTaskAction> getTaskActions();
+
     Spec<? super TaskInternal> getOnlyIf();
 
     void execute();
diff --git a/subprojects/core/src/main/groovy/org/gradle/api/internal/TaskOutputsInternal.java b/subprojects/core/src/main/groovy/org/gradle/api/internal/TaskOutputsInternal.java
index 5a079cb..1ff75d1 100755
--- a/subprojects/core/src/main/groovy/org/gradle/api/internal/TaskOutputsInternal.java
+++ b/subprojects/core/src/main/groovy/org/gradle/api/internal/TaskOutputsInternal.java
@@ -26,4 +26,5 @@ public interface TaskOutputsInternal extends TaskOutputs {
     FileCollection getPreviousFiles();
 
     void setHistory(TaskExecutionHistory history);
+
 }
diff --git a/subprojects/core/src/main/groovy/org/gradle/api/internal/artifacts/ArtifactPublicationServices.java b/subprojects/core/src/main/groovy/org/gradle/api/internal/artifacts/ArtifactPublicationServices.java
index 5291a1f..52d4a7a 100644
--- a/subprojects/core/src/main/groovy/org/gradle/api/internal/artifacts/ArtifactPublicationServices.java
+++ b/subprojects/core/src/main/groovy/org/gradle/api/internal/artifacts/ArtifactPublicationServices.java
@@ -16,16 +16,9 @@
 package org.gradle.api.internal.artifacts;
 
 import org.gradle.api.artifacts.dsl.RepositoryHandler;
-import org.gradle.api.internal.artifacts.ivyservice.IvyModuleDescriptorWriter;
-import org.gradle.api.internal.artifacts.ivyservice.ModuleDescriptorConverter;
 
 public interface ArtifactPublicationServices {
-
     RepositoryHandler createRepositoryHandler();
 
-    ModuleDescriptorConverter getDescriptorFileModuleConverter();
-
-    IvyModuleDescriptorWriter getIvyModuleDescriptorWriter();
-
     ArtifactPublisher createArtifactPublisher();
 }
diff --git a/subprojects/core/src/main/groovy/org/gradle/api/internal/artifacts/ArtifactPublisher.java b/subprojects/core/src/main/groovy/org/gradle/api/internal/artifacts/ArtifactPublisher.java
index 64f14c0..8d754ee 100644
--- a/subprojects/core/src/main/groovy/org/gradle/api/internal/artifacts/ArtifactPublisher.java
+++ b/subprojects/core/src/main/groovy/org/gradle/api/internal/artifacts/ArtifactPublisher.java
@@ -23,9 +23,6 @@ import org.gradle.api.internal.artifacts.repositories.PublicationAwareRepository
 import java.io.File;
 import java.util.Set;
 
-/**
- * @author Hans Dockter
- */
 public interface ArtifactPublisher {
     void publish(Iterable<? extends PublicationAwareRepository> repositories, Module module, Set<? extends Configuration> configurations, File descriptor) throws PublishException;
 }
diff --git a/subprojects/core/src/main/groovy/org/gradle/api/internal/artifacts/BaseRepositoryFactory.java b/subprojects/core/src/main/groovy/org/gradle/api/internal/artifacts/BaseRepositoryFactory.java
index b265e39..d8ac5a1 100644
--- a/subprojects/core/src/main/groovy/org/gradle/api/internal/artifacts/BaseRepositoryFactory.java
+++ b/subprojects/core/src/main/groovy/org/gradle/api/internal/artifacts/BaseRepositoryFactory.java
@@ -32,6 +32,8 @@ public interface BaseRepositoryFactory {
 
     MavenArtifactRepository createMavenLocalRepository();
 
+    MavenArtifactRepository createJCenterRepository();
+
     MavenArtifactRepository createMavenCentralRepository();
 
     IvyArtifactRepository createIvyRepository();
diff --git a/subprojects/core/src/main/groovy/org/gradle/api/internal/artifacts/CachingDependencyResolveContext.java b/subprojects/core/src/main/groovy/org/gradle/api/internal/artifacts/CachingDependencyResolveContext.java
index 660bca5..ce4ef94 100644
--- a/subprojects/core/src/main/groovy/org/gradle/api/internal/artifacts/CachingDependencyResolveContext.java
+++ b/subprojects/core/src/main/groovy/org/gradle/api/internal/artifacts/CachingDependencyResolveContext.java
@@ -17,8 +17,8 @@
 package org.gradle.api.internal.artifacts;
 
 import org.gradle.api.file.FileCollection;
-import org.gradle.api.internal.CachingDirectedGraphWalker;
-import org.gradle.api.internal.DirectedGraph;
+import org.gradle.internal.graph.CachingDirectedGraphWalker;
+import org.gradle.internal.graph.DirectedGraph;
 import org.gradle.api.internal.file.UnionFileCollection;
 
 import java.util.ArrayList;
@@ -52,7 +52,7 @@ public class CachingDependencyResolveContext implements DependencyResolveContext
     }
 
     private class DependencyGraph implements DirectedGraph<Object, FileCollection> {
-        public void getNodeValues(Object node, Collection<FileCollection> values, Collection<Object> connectedNodes) {
+        public void getNodeValues(Object node, Collection<? super FileCollection> values, Collection<? super Object> connectedNodes) {
             if (node instanceof FileCollection) {
                 FileCollection fileCollection = (FileCollection) node;
                 values.add(fileCollection);
diff --git a/subprojects/core/src/main/groovy/org/gradle/api/internal/artifacts/DefaultArtifactIdentifier.java b/subprojects/core/src/main/groovy/org/gradle/api/internal/artifacts/DefaultArtifactIdentifier.java
deleted file mode 100644
index b9cbe72..0000000
--- a/subprojects/core/src/main/groovy/org/gradle/api/internal/artifacts/DefaultArtifactIdentifier.java
+++ /dev/null
@@ -1,55 +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.api.internal.artifacts;
-
-import org.gradle.api.artifacts.ArtifactIdentifier;
-import org.gradle.api.artifacts.ModuleVersionIdentifier;
-
-public class DefaultArtifactIdentifier implements ArtifactIdentifier {
-    private final ModuleVersionIdentifier moduleVersionIdentifier;
-    private final String name;
-    private final String type;
-    private final String extension;
-    private final String classifier;
-
-    public DefaultArtifactIdentifier(ModuleVersionIdentifier moduleVersionIdentifier, String name, String type, String extension, String classifier) {
-        this.moduleVersionIdentifier = moduleVersionIdentifier;
-        this.name = name;
-        this.type = type;
-        this.extension = extension;
-        this.classifier = classifier;
-    }
-
-    public ModuleVersionIdentifier getModuleVersionIdentifier() {
-        return moduleVersionIdentifier;
-    }
-
-    public String getName() {
-        return name;
-    }
-
-    public String getType() {
-        return type;
-    }
-
-    public String getExtension() {
-        return extension;
-    }
-
-    public String getClassifier() {
-        return classifier;
-    }
-}
diff --git a/subprojects/core/src/main/groovy/org/gradle/api/internal/artifacts/DefaultArtifactRepositoryContainer.java b/subprojects/core/src/main/groovy/org/gradle/api/internal/artifacts/DefaultArtifactRepositoryContainer.java
index a5badfc..eef5be5 100644
--- a/subprojects/core/src/main/groovy/org/gradle/api/internal/artifacts/DefaultArtifactRepositoryContainer.java
+++ b/subprojects/core/src/main/groovy/org/gradle/api/internal/artifacts/DefaultArtifactRepositoryContainer.java
@@ -38,9 +38,6 @@ import java.util.List;
 
 import static org.gradle.api.internal.Cast.cast;
 
-/**
- * @author Hans Dockter
- */
 public class DefaultArtifactRepositoryContainer extends DefaultNamedDomainObjectList<ArtifactRepository>
         implements ArtifactRepositoryContainer {
 
@@ -85,11 +82,13 @@ public class DefaultArtifactRepositoryContainer extends DefaultNamedDomainObject
     }
 
     public boolean add(DependencyResolver resolver, Closure configureClosure) {
+        DeprecationLogger.nagUserOfDiscontinuedMethod("ArtifactRepositoryContainer.add(DependencyResolver, Closure)");
         addCustomDependencyResolver(resolver, configureClosure, addLastAction);
         return true;
     }
 
     public boolean add(DependencyResolver resolver) {
+        DeprecationLogger.nagUserOfDiscontinuedMethod("ArtifactRepositoryContainer.add(DependencyResolver)");
         addCustomDependencyResolver(resolver, null, addLastAction);
         return true;
     }
@@ -99,6 +98,7 @@ public class DefaultArtifactRepositoryContainer extends DefaultNamedDomainObject
     }
 
     public DependencyResolver addFirst(Object userDescription, Closure configureClosure) {
+        DeprecationLogger.nagUserOfReplacedMethod("ArtifactRepositoryContainer.addFirst(Object)", "addFirst(ArtifactRepository");
         return addCustomDependencyResolver(userDescription, configureClosure, addFirstAction);
     }
 
@@ -122,6 +122,7 @@ public class DefaultArtifactRepositoryContainer extends DefaultNamedDomainObject
         if (!GUtil.isTrue(afterResolverName)) {
             throw new InvalidUserDataException("You must specify afterResolverName");
         }
+        DeprecationLogger.nagUserOfDiscontinuedMethod("ArtifactRepositoryContainer.addBefore()");
         final ArtifactRepository after = getByName(afterResolverName);
         return addCustomDependencyResolver(userDescription, configureClosure, new Action<ArtifactRepository>() {
             public void execute(ArtifactRepository repository) {
@@ -138,6 +139,7 @@ public class DefaultArtifactRepositoryContainer extends DefaultNamedDomainObject
         if (!GUtil.isTrue(beforeResolverName)) {
             throw new InvalidUserDataException("You must specify beforeResolverName");
         }
+        DeprecationLogger.nagUserOfDiscontinuedMethod("ArtifactRepositoryContainer.addAfter()");
         final ArtifactRepository before = getByName(beforeResolverName);
 
         return addCustomDependencyResolver(userDescription, configureClosure, new Action<ArtifactRepository>() {
diff --git a/subprojects/core/src/main/groovy/org/gradle/api/internal/artifacts/DefaultExcludeRule.java b/subprojects/core/src/main/groovy/org/gradle/api/internal/artifacts/DefaultExcludeRule.java
index 244f338..249e3bd 100644
--- a/subprojects/core/src/main/groovy/org/gradle/api/internal/artifacts/DefaultExcludeRule.java
+++ b/subprojects/core/src/main/groovy/org/gradle/api/internal/artifacts/DefaultExcludeRule.java
@@ -21,11 +21,6 @@ import org.gradle.util.DeprecationLogger;
 import java.util.HashMap;
 import java.util.Map;
 
-/**
- * @author Hans Dockter
- *
- *         DefaultExcludeRule is a value object
- */
 public class DefaultExcludeRule implements ExcludeRule {
     private String group;
     private String module;
diff --git a/subprojects/core/src/main/groovy/org/gradle/api/internal/artifacts/DefaultExcludeRuleContainer.java b/subprojects/core/src/main/groovy/org/gradle/api/internal/artifacts/DefaultExcludeRuleContainer.java
index ebae94d..3974bb5 100644
--- a/subprojects/core/src/main/groovy/org/gradle/api/internal/artifacts/DefaultExcludeRuleContainer.java
+++ b/subprojects/core/src/main/groovy/org/gradle/api/internal/artifacts/DefaultExcludeRuleContainer.java
@@ -24,9 +24,6 @@ import java.util.LinkedHashSet;
 import java.util.Map;
 import java.util.Set;
 
-/**
- * @author Hans Dockter
- */
 public class DefaultExcludeRuleContainer implements ExcludeRuleContainer {
     private Set<ExcludeRule> addedRules = new LinkedHashSet<ExcludeRule>();
     private NotationParser<ExcludeRule> notationParser = new ExcludeRuleNotationParser<ExcludeRule>();
diff --git a/subprojects/core/src/main/groovy/org/gradle/api/internal/artifacts/DefaultModule.java b/subprojects/core/src/main/groovy/org/gradle/api/internal/artifacts/DefaultModule.java
index 5190958..a46cea5 100644
--- a/subprojects/core/src/main/groovy/org/gradle/api/internal/artifacts/DefaultModule.java
+++ b/subprojects/core/src/main/groovy/org/gradle/api/internal/artifacts/DefaultModule.java
@@ -17,9 +17,6 @@ package org.gradle.api.internal.artifacts;
 
 import org.gradle.api.artifacts.Module;
 
-/**
- * @author Hans Dockter
- */
 public class DefaultModule implements Module {
     private String group;
     private String name;
diff --git a/subprojects/core/src/main/groovy/org/gradle/api/internal/artifacts/DependencyResolutionServices.java b/subprojects/core/src/main/groovy/org/gradle/api/internal/artifacts/DependencyResolutionServices.java
index 8aa927a..5e6493c 100644
--- a/subprojects/core/src/main/groovy/org/gradle/api/internal/artifacts/DependencyResolutionServices.java
+++ b/subprojects/core/src/main/groovy/org/gradle/api/internal/artifacts/DependencyResolutionServices.java
@@ -17,6 +17,7 @@ package org.gradle.api.internal.artifacts;
 
 import org.gradle.api.artifacts.dsl.ArtifactHandler;
 import org.gradle.api.artifacts.dsl.DependencyHandler;
+import org.gradle.api.artifacts.dsl.ComponentMetadataHandler;
 import org.gradle.api.artifacts.dsl.RepositoryHandler;
 import org.gradle.api.internal.artifacts.configurations.ConfigurationContainerInternal;
 
@@ -27,6 +28,8 @@ public interface DependencyResolutionServices {
 
     DependencyHandler getDependencyHandler();
 
+    ComponentMetadataHandler getComponentMetadataHandler();
+
     ArtifactHandler getArtifactHandler();
 
     ArtifactPublicationServices createArtifactPublicationServices();
diff --git a/subprojects/core/src/main/groovy/org/gradle/api/internal/artifacts/DependencyResolveDetailsInternal.java b/subprojects/core/src/main/groovy/org/gradle/api/internal/artifacts/DependencyResolveDetailsInternal.java
index 15738f2..89c29d2 100644
--- a/subprojects/core/src/main/groovy/org/gradle/api/internal/artifacts/DependencyResolveDetailsInternal.java
+++ b/subprojects/core/src/main/groovy/org/gradle/api/internal/artifacts/DependencyResolveDetailsInternal.java
@@ -19,9 +19,6 @@ package org.gradle.api.internal.artifacts;
 import org.gradle.api.artifacts.DependencyResolveDetails;
 import org.gradle.api.artifacts.result.ModuleVersionSelectionReason;
 
-/**
- * by Szczepan Faber, created at: 12/13/12
- */
 public interface DependencyResolveDetailsInternal extends DependencyResolveDetails {
 
     void useVersion(String version, ModuleVersionSelectionReason selectionReason);
diff --git a/subprojects/core/src/main/groovy/org/gradle/api/internal/artifacts/ExcludeRuleNotationParser.java b/subprojects/core/src/main/groovy/org/gradle/api/internal/artifacts/ExcludeRuleNotationParser.java
index a135639..0110359 100644
--- a/subprojects/core/src/main/groovy/org/gradle/api/internal/artifacts/ExcludeRuleNotationParser.java
+++ b/subprojects/core/src/main/groovy/org/gradle/api/internal/artifacts/ExcludeRuleNotationParser.java
@@ -24,9 +24,6 @@ import org.gradle.api.tasks.Optional;
 
 import java.util.Collection;
 
-/**
- * @author Rene Groeschke
- */
 public class ExcludeRuleNotationParser<T extends ExcludeRule> extends MapNotationParser<T> {
 
     @Override
diff --git a/subprojects/core/src/main/groovy/org/gradle/api/internal/artifacts/ModuleVersionPublishMetaData.java b/subprojects/core/src/main/groovy/org/gradle/api/internal/artifacts/ModuleVersionPublishMetaData.java
new file mode 100644
index 0000000..da784fb
--- /dev/null
+++ b/subprojects/core/src/main/groovy/org/gradle/api/internal/artifacts/ModuleVersionPublishMetaData.java
@@ -0,0 +1,32 @@
+/*
+ * Copyright 2013 the original author or authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY 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.apache.ivy.core.module.descriptor.Artifact;
+import org.apache.ivy.core.module.descriptor.ModuleDescriptor;
+import org.gradle.api.artifacts.ModuleVersionIdentifier;
+
+import java.io.File;
+import java.util.Map;
+
+public interface ModuleVersionPublishMetaData {
+    ModuleVersionIdentifier getId();
+
+    ModuleDescriptor getModuleDescriptor();
+
+    Map<Artifact, File> getArtifacts();
+}
diff --git a/subprojects/core/src/main/groovy/org/gradle/api/internal/artifacts/ModuleVersionPublisher.java b/subprojects/core/src/main/groovy/org/gradle/api/internal/artifacts/ModuleVersionPublisher.java
new file mode 100644
index 0000000..cb2a955
--- /dev/null
+++ b/subprojects/core/src/main/groovy/org/gradle/api/internal/artifacts/ModuleVersionPublisher.java
@@ -0,0 +1,27 @@
+/*
+ * Copyright 2012 the original author or authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY 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.apache.ivy.core.settings.IvySettings;
+
+import java.io.IOException;
+
+public interface ModuleVersionPublisher {
+    void publish(ModuleVersionPublishMetaData moduleVersion) throws IOException;
+
+    void setSettings(IvySettings settings);
+}
diff --git a/subprojects/core/src/main/groovy/org/gradle/api/internal/artifacts/ModuleVersionSelectorStrictSpec.java b/subprojects/core/src/main/groovy/org/gradle/api/internal/artifacts/ModuleVersionSelectorStrictSpec.java
index 91c395e..93f2169 100644
--- a/subprojects/core/src/main/groovy/org/gradle/api/internal/artifacts/ModuleVersionSelectorStrictSpec.java
+++ b/subprojects/core/src/main/groovy/org/gradle/api/internal/artifacts/ModuleVersionSelectorStrictSpec.java
@@ -20,9 +20,6 @@ import org.gradle.api.artifacts.ModuleVersionIdentifier;
 import org.gradle.api.artifacts.ModuleVersionSelector;
 import org.gradle.api.specs.Spec;
 
-/**
- * by Szczepan Faber, created at: 9/10/12
- */
 public class ModuleVersionSelectorStrictSpec implements Spec<ModuleVersionIdentifier> {
 
     private final ModuleVersionSelector selector;
diff --git a/subprojects/core/src/main/groovy/org/gradle/api/internal/artifacts/configurations/ConfigurationInternal.java b/subprojects/core/src/main/groovy/org/gradle/api/internal/artifacts/configurations/ConfigurationInternal.java
index 6beda87..2995f7d 100644
--- a/subprojects/core/src/main/groovy/org/gradle/api/internal/artifacts/configurations/ConfigurationInternal.java
+++ b/subprojects/core/src/main/groovy/org/gradle/api/internal/artifacts/configurations/ConfigurationInternal.java
@@ -22,4 +22,6 @@ public interface ConfigurationInternal extends Configuration, DependencyMetaData
     DependencyResolutionListener getDependencyResolutionBroadcast();
 
     ResolutionStrategyInternal getResolutionStrategy();
+
+    String getPath();
 }
\ No newline at end of file
diff --git a/subprojects/core/src/main/groovy/org/gradle/api/internal/artifacts/configurations/DependencyMetaDataProvider.java b/subprojects/core/src/main/groovy/org/gradle/api/internal/artifacts/configurations/DependencyMetaDataProvider.java
index a5b6ef0..88bc9af 100644
--- a/subprojects/core/src/main/groovy/org/gradle/api/internal/artifacts/configurations/DependencyMetaDataProvider.java
+++ b/subprojects/core/src/main/groovy/org/gradle/api/internal/artifacts/configurations/DependencyMetaDataProvider.java
@@ -17,9 +17,6 @@ package org.gradle.api.internal.artifacts.configurations;
 
 import org.gradle.api.artifacts.Module;
 
-/**
- * @author Hans Dockter
- */
 public interface DependencyMetaDataProvider {
     Module getModule();
 }
diff --git a/subprojects/core/src/main/groovy/org/gradle/api/internal/artifacts/dependencies/AbstractDependency.java b/subprojects/core/src/main/groovy/org/gradle/api/internal/artifacts/dependencies/AbstractDependency.java
index 07dfb00..f4d0922 100644
--- a/subprojects/core/src/main/groovy/org/gradle/api/internal/artifacts/dependencies/AbstractDependency.java
+++ b/subprojects/core/src/main/groovy/org/gradle/api/internal/artifacts/dependencies/AbstractDependency.java
@@ -17,12 +17,9 @@
 package org.gradle.api.internal.artifacts.dependencies;
 
 import org.gradle.api.artifacts.Dependency;
-import org.gradle.api.internal.artifacts.ResolvableDependency;
 import org.gradle.api.internal.artifacts.DependencyResolveContext;
+import org.gradle.api.internal.artifacts.ResolvableDependency;
 
-/**
-* @author Hans Dockter
-*/
 public abstract class AbstractDependency implements ResolvableDependency, Dependency {
     protected void copyTo(AbstractDependency target) {
     }
diff --git a/subprojects/core/src/main/groovy/org/gradle/api/internal/artifacts/dependencies/DefaultClientModule.java b/subprojects/core/src/main/groovy/org/gradle/api/internal/artifacts/dependencies/DefaultClientModule.java
index c0cda2b..3de7134 100644
--- a/subprojects/core/src/main/groovy/org/gradle/api/internal/artifacts/dependencies/DefaultClientModule.java
+++ b/subprojects/core/src/main/groovy/org/gradle/api/internal/artifacts/dependencies/DefaultClientModule.java
@@ -24,9 +24,6 @@ import org.gradle.api.artifacts.ModuleDependency;
 import java.util.HashSet;
 import java.util.Set;
 
-/**
- * @author Hans Dockter
- */
 public class DefaultClientModule extends AbstractExternalDependency implements ClientModule {
 
     private String group;
diff --git a/subprojects/core/src/main/groovy/org/gradle/api/internal/artifacts/dependencies/DefaultExternalModuleDependency.java b/subprojects/core/src/main/groovy/org/gradle/api/internal/artifacts/dependencies/DefaultExternalModuleDependency.java
index 9ed7976..4de8446 100644
--- a/subprojects/core/src/main/groovy/org/gradle/api/internal/artifacts/dependencies/DefaultExternalModuleDependency.java
+++ b/subprojects/core/src/main/groovy/org/gradle/api/internal/artifacts/dependencies/DefaultExternalModuleDependency.java
@@ -20,9 +20,6 @@ import org.gradle.api.InvalidUserDataException;
 import org.gradle.api.artifacts.Dependency;
 import org.gradle.api.artifacts.ExternalModuleDependency;
 
-/**
- * @author Hans Dockter
- */
 public class DefaultExternalModuleDependency extends AbstractExternalDependency implements ExternalModuleDependency {
     private String group;
     private String name;
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 76f9bde..62c2acd 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
@@ -31,10 +31,7 @@ import org.gradle.initialization.ProjectAccessListener;
 import java.io.File;
 import java.util.Set;
 
-/**
- * @author Hans Dockter
- */
-public class DefaultProjectDependency extends AbstractModuleDependency implements ProjectDependency {
+public class DefaultProjectDependency extends AbstractModuleDependency implements ProjectDependencyInternal {
     private ProjectInternal dependencyProject;
     private final boolean buildProjectDependencies;
     private final TaskDependencyImpl taskDependency = new TaskDependencyImpl();
@@ -89,6 +86,10 @@ public class DefaultProjectDependency extends AbstractModuleDependency implement
         return context.resolve().getFiles();
     }
 
+    public void beforeResolved() {
+        projectAccessListener.beforeResolvingProjectDependency(dependencyProject);
+    }
+
     @Override
     public void resolve(DependencyResolveContext context) {
         boolean transitive = isTransitive() && context.isTransitive();
diff --git a/subprojects/core/src/main/groovy/org/gradle/api/internal/artifacts/dependencies/ProjectDependencyInternal.java b/subprojects/core/src/main/groovy/org/gradle/api/internal/artifacts/dependencies/ProjectDependencyInternal.java
new file mode 100644
index 0000000..a1236f3
--- /dev/null
+++ b/subprojects/core/src/main/groovy/org/gradle/api/internal/artifacts/dependencies/ProjectDependencyInternal.java
@@ -0,0 +1,28 @@
+/*
+ * Copyright 2013 the original author or authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY 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.dependencies;
+
+import org.gradle.api.artifacts.ProjectDependency;
+
+public interface ProjectDependencyInternal extends ProjectDependency {
+
+    /**
+     * This method is called when the project dependency is resolved
+     */
+    public void beforeResolved();
+
+}
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 a7058ad..1dde756 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
@@ -36,11 +36,11 @@ import java.util.Map;
 
 import static org.gradle.util.CollectionUtils.flattenToList;
 
-/**
- * @author Hans Dockter
- */
 public class DefaultRepositoryHandler extends DefaultArtifactRepositoryContainer implements RepositoryHandler {
 
+    public static final String DEFAULT_BINTRAY_JCENTER_REPO_NAME = "BintrayJCenter";
+    public static final String BINTRAY_JCENTER_URL = "http://jcenter.bintray.com/";
+
     public static final String FLAT_DIR_DEFAULT_NAME = "flatDir";
     private static final String MAVEN_REPO_DEFAULT_NAME = "maven";
     private static final String IVY_REPO_DEFAULT_NAME = "ivy";
@@ -72,6 +72,14 @@ public class DefaultRepositoryHandler extends DefaultArtifactRepositoryContainer
         return addRepository(repositoryFactory.createMavenCentralRepository(), DEFAULT_MAVEN_CENTRAL_REPO_NAME);
     }
 
+    public MavenArtifactRepository jcenter() {
+        return addRepository(repositoryFactory.createJCenterRepository(), DEFAULT_BINTRAY_JCENTER_REPO_NAME);
+    }
+
+    public MavenArtifactRepository jcenter(Action<? super MavenArtifactRepository> action) {
+        return addRepository(repositoryFactory.createJCenterRepository(), DEFAULT_BINTRAY_JCENTER_REPO_NAME, action);
+    }
+
     public MavenArtifactRepository mavenCentral(Map<String, ?> args) {
         Map<String, Object> modifiedArgs = new HashMap<String, Object>(args);
         if (modifiedArgs.containsKey("urls")) {
@@ -95,14 +103,11 @@ public class DefaultRepositoryHandler extends DefaultArtifactRepositoryContainer
     }
 
     public DependencyResolver mavenRepo(Map<String, ?> args, Closure configClosure) {
+        DeprecationLogger.nagUserOfReplacedMethod("RepositoryHandler.mavenRepo()", "maven()");
         Map<String, Object> modifiedArgs = new HashMap<String, Object>(args);
         if (modifiedArgs.containsKey("urls")) {
             List<?> urls = flattenToList(modifiedArgs.remove("urls"));
             if (!urls.isEmpty()) {
-                DeprecationLogger.nagUserOfDeprecated(
-                        "The 'urls' property of the RepositoryHandler.mavenRepo() method",
-                        "You should use the 'url' property to define the core maven repository & the 'artifactUrls' property to define any additional artifact locations"
-                );
                 modifiedArgs.put("url", urls.get(0));
                 List<?> extraUrls = urls.subList(1, urls.size());
                 modifiedArgs.put("artifactUrls", extraUrls);
diff --git a/subprojects/core/src/main/groovy/org/gradle/api/internal/artifacts/dsl/dependencies/DefaultDependencyHandler.groovy b/subprojects/core/src/main/groovy/org/gradle/api/internal/artifacts/dsl/dependencies/DefaultDependencyHandler.groovy
index 9cb99db..c8b0528 100644
--- a/subprojects/core/src/main/groovy/org/gradle/api/internal/artifacts/dsl/dependencies/DefaultDependencyHandler.groovy
+++ b/subprojects/core/src/main/groovy/org/gradle/api/internal/artifacts/dsl/dependencies/DefaultDependencyHandler.groovy
@@ -19,23 +19,23 @@ package org.gradle.api.internal.artifacts.dsl.dependencies
 import org.gradle.api.artifacts.Configuration
 import org.gradle.api.artifacts.ConfigurationContainer
 import org.gradle.api.artifacts.Dependency
+import org.gradle.api.artifacts.dsl.ComponentMetadataHandler
 import org.gradle.api.artifacts.dsl.DependencyHandler
 import org.gradle.util.ConfigureUtil
 import org.gradle.util.GUtil
 
-/**
- * @author Hans Dockter
- */
 class DefaultDependencyHandler implements DependencyHandler {
-    ConfigurationContainer configurationContainer
-    DependencyFactory dependencyFactory
-    ProjectFinder projectFinder
+    private final ConfigurationContainer configurationContainer
+    private final DependencyFactory dependencyFactory
+    private final ProjectFinder projectFinder
+    private final ComponentMetadataHandler metadataHandler
 
     def DefaultDependencyHandler(ConfigurationContainer configurationContainer, DependencyFactory dependencyFactory,
-                                 ProjectFinder projectFinder) {
+                                 ProjectFinder projectFinder, ComponentMetadataHandler metadataHandler) {
         this.configurationContainer = configurationContainer
         this.dependencyFactory = dependencyFactory
         this.projectFinder = projectFinder
+        this.metadataHandler = metadataHandler
     }
 
     public Dependency add(String configurationName, Object dependencyNotation) {
@@ -106,4 +106,13 @@ class DefaultDependencyHandler implements DependencyHandler {
         }
         return null;
     }
+
+    public void components(Closure configureClosure) {
+        ConfigureUtil.configure(configureClosure, getComponents());
+    }
+
+    public ComponentMetadataHandler getComponents() {
+        return metadataHandler;
+    }
+
 }
\ No newline at end of file
diff --git a/subprojects/core/src/main/groovy/org/gradle/api/internal/artifacts/dsl/dependencies/DependencyFactory.java b/subprojects/core/src/main/groovy/org/gradle/api/internal/artifacts/dsl/dependencies/DependencyFactory.java
index 890c4ec..b2ba01f 100644
--- a/subprojects/core/src/main/groovy/org/gradle/api/internal/artifacts/dsl/dependencies/DependencyFactory.java
+++ b/subprojects/core/src/main/groovy/org/gradle/api/internal/artifacts/dsl/dependencies/DependencyFactory.java
@@ -22,9 +22,6 @@ import org.gradle.api.artifacts.ProjectDependency;
 
 import java.util.Map;
 
-/**
- * @author Hans Dockter
- */
 public interface DependencyFactory {
     //for gradle distribution specific dependencies
     enum ClassPathNotation {
diff --git a/subprojects/core/src/main/groovy/org/gradle/api/internal/artifacts/dsl/dependencies/ModuleDescriptorDelegate.groovy b/subprojects/core/src/main/groovy/org/gradle/api/internal/artifacts/dsl/dependencies/ModuleDescriptorDelegate.groovy
index 4ea3563..513ebb8 100644
--- a/subprojects/core/src/main/groovy/org/gradle/api/internal/artifacts/dsl/dependencies/ModuleDescriptorDelegate.groovy
+++ b/subprojects/core/src/main/groovy/org/gradle/api/internal/artifacts/dsl/dependencies/ModuleDescriptorDelegate.groovy
@@ -19,9 +19,6 @@ package org.gradle.api.internal.artifacts.dsl.dependencies
 import org.gradle.api.artifacts.ClientModule
 import org.gradle.util.ConfigureUtil
 
-/**
- * @author Hans Dockter
- */
 class ModuleFactoryDelegate {
   ClientModule clientModule
   DependencyFactory dependencyFactory
diff --git a/subprojects/core/src/main/groovy/org/gradle/api/internal/artifacts/dsl/dependencies/ModuleFactoryHelper.java b/subprojects/core/src/main/groovy/org/gradle/api/internal/artifacts/dsl/dependencies/ModuleFactoryHelper.java
index f9ce356..1946e14 100644
--- a/subprojects/core/src/main/groovy/org/gradle/api/internal/artifacts/dsl/dependencies/ModuleFactoryHelper.java
+++ b/subprojects/core/src/main/groovy/org/gradle/api/internal/artifacts/dsl/dependencies/ModuleFactoryHelper.java
@@ -15,13 +15,10 @@
  */
 package org.gradle.api.internal.artifacts.dsl.dependencies;
 
-import org.gradle.api.artifacts.ExternalDependency;
 import org.gradle.api.artifacts.DependencyArtifact;
+import org.gradle.api.artifacts.ExternalDependency;
 import org.gradle.api.internal.artifacts.dependencies.DefaultDependencyArtifact;
 
-/**
- * @author Hans Dockter
- */
 public class ModuleFactoryHelper {
     public static void addExplicitArtifactsIfDefined(ExternalDependency moduleDependency, String artifactType, String classifier) {
         String actualArtifactType = artifactType;
diff --git a/subprojects/core/src/main/groovy/org/gradle/api/internal/artifacts/dsl/dependencies/ProjectFinder.java b/subprojects/core/src/main/groovy/org/gradle/api/internal/artifacts/dsl/dependencies/ProjectFinder.java
index 12f736c..440b513 100644
--- a/subprojects/core/src/main/groovy/org/gradle/api/internal/artifacts/dsl/dependencies/ProjectFinder.java
+++ b/subprojects/core/src/main/groovy/org/gradle/api/internal/artifacts/dsl/dependencies/ProjectFinder.java
@@ -17,9 +17,6 @@ package org.gradle.api.internal.artifacts.dsl.dependencies;
 
 import org.gradle.api.internal.project.ProjectInternal;
 
-/**
- * @author Hans Dockter
-*/
 public interface ProjectFinder {
     /**
      *
diff --git a/subprojects/core/src/main/groovy/org/gradle/api/internal/artifacts/ivyservice/ModuleDescriptorConverter.java b/subprojects/core/src/main/groovy/org/gradle/api/internal/artifacts/ivyservice/ModuleDescriptorConverter.java
deleted file mode 100644
index 4b4a995..0000000
--- a/subprojects/core/src/main/groovy/org/gradle/api/internal/artifacts/ivyservice/ModuleDescriptorConverter.java
+++ /dev/null
@@ -1,35 +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.ivyservice;
-
-import org.apache.ivy.core.module.descriptor.DefaultModuleDescriptor;
-import org.apache.ivy.core.module.descriptor.ModuleDescriptor;
-import org.gradle.api.artifacts.Configuration;
-import org.gradle.api.artifacts.Module;
-import org.gradle.api.artifacts.ModuleDependency;
-
-import java.util.Set;
-
-/**
- * @author Hans Dockter
- */
-public interface ModuleDescriptorConverter {
-    ModuleDescriptor convert(Set<? extends Configuration> configurations, Module module);
-
-    ModuleDescriptor createModuleDescriptor(Module module);
-
-    void addDependencyDescriptor(String configuration, DefaultModuleDescriptor moduleDescriptor, ModuleDependency dependency);
-}
diff --git a/subprojects/core/src/main/groovy/org/gradle/api/internal/artifacts/publish/AbstractPublishArtifact.java b/subprojects/core/src/main/groovy/org/gradle/api/internal/artifacts/publish/AbstractPublishArtifact.java
index abdc592..46e8df8 100644
--- a/subprojects/core/src/main/groovy/org/gradle/api/internal/artifacts/publish/AbstractPublishArtifact.java
+++ b/subprojects/core/src/main/groovy/org/gradle/api/internal/artifacts/publish/AbstractPublishArtifact.java
@@ -19,9 +19,6 @@ import org.gradle.api.artifacts.PublishArtifact;
 import org.gradle.api.internal.tasks.DefaultTaskDependency;
 import org.gradle.api.tasks.TaskDependency;
 
-/**
- * @author Hans Dockter
- */
 public abstract class AbstractPublishArtifact implements PublishArtifact {
     private final DefaultTaskDependency taskDependency;
 
diff --git a/subprojects/core/src/main/groovy/org/gradle/api/internal/artifacts/publish/ArchivePublishArtifact.java b/subprojects/core/src/main/groovy/org/gradle/api/internal/artifacts/publish/ArchivePublishArtifact.java
index eb4fa9a..aef35f5 100644
--- a/subprojects/core/src/main/groovy/org/gradle/api/internal/artifacts/publish/ArchivePublishArtifact.java
+++ b/subprojects/core/src/main/groovy/org/gradle/api/internal/artifacts/publish/ArchivePublishArtifact.java
@@ -21,9 +21,6 @@ import org.gradle.util.GUtil;
 import java.io.File;
 import java.util.Date;
 
-/**
- * @author Hans Dockter
- */
 public class ArchivePublishArtifact extends AbstractPublishArtifact {
     private String name;
     private String extension;
diff --git a/subprojects/core/src/main/groovy/org/gradle/api/internal/artifacts/publish/DefaultPublishArtifact.java b/subprojects/core/src/main/groovy/org/gradle/api/internal/artifacts/publish/DefaultPublishArtifact.java
index 8591a4c..092bd3e 100644
--- a/subprojects/core/src/main/groovy/org/gradle/api/internal/artifacts/publish/DefaultPublishArtifact.java
+++ b/subprojects/core/src/main/groovy/org/gradle/api/internal/artifacts/publish/DefaultPublishArtifact.java
@@ -21,9 +21,6 @@ import org.gradle.api.artifacts.ConfigurablePublishArtifact;
 import java.io.File;
 import java.util.Date;
 
-/**
- * @author Hans Dockter
- */
 public class DefaultPublishArtifact extends AbstractPublishArtifact implements ConfigurablePublishArtifact {
     private String name;
     private String extension;
diff --git a/subprojects/core/src/main/groovy/org/gradle/api/internal/artifacts/repositories/PublicationAwareRepository.java b/subprojects/core/src/main/groovy/org/gradle/api/internal/artifacts/repositories/PublicationAwareRepository.java
index 9c493a5..4bed715 100644
--- a/subprojects/core/src/main/groovy/org/gradle/api/internal/artifacts/repositories/PublicationAwareRepository.java
+++ b/subprojects/core/src/main/groovy/org/gradle/api/internal/artifacts/repositories/PublicationAwareRepository.java
@@ -16,8 +16,8 @@
 
 package org.gradle.api.internal.artifacts.repositories;
 
-import org.apache.ivy.plugins.resolver.DependencyResolver;
+import org.gradle.api.internal.artifacts.ModuleVersionPublisher;
 
 public interface PublicationAwareRepository {
-    DependencyResolver createPublisher();
+    ModuleVersionPublisher createPublisher();
 }
diff --git a/subprojects/core/src/main/groovy/org/gradle/api/internal/artifacts/version/LatestVersionSemanticComparator.java b/subprojects/core/src/main/groovy/org/gradle/api/internal/artifacts/version/LatestVersionSemanticComparator.java
deleted file mode 100644
index 2ea8205..0000000
--- a/subprojects/core/src/main/groovy/org/gradle/api/internal/artifacts/version/LatestVersionSemanticComparator.java
+++ /dev/null
@@ -1,49 +0,0 @@
-/*
- * Copyright 2012 the original author or authors.
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- *      http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY 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.version;
-
-import org.apache.ivy.plugins.latest.ArtifactInfo;
-import org.apache.ivy.plugins.latest.LatestRevisionStrategy;
-
-import java.util.Comparator;
-
-/**
- * by Szczepan Faber, created at: 10/9/12
- */
-public class LatestVersionSemanticComparator implements Comparator<String> {
-
-    public int compare(String left, String right) {
-        return new LatestRevisionStrategy().getComparator().compare(new SimpleArtifactInfo(left), new SimpleArtifactInfo(right));
-    }
-
-    private static class SimpleArtifactInfo implements ArtifactInfo {
-
-        private final String version;
-
-        public SimpleArtifactInfo(String version) {
-            this.version = version;
-        }
-
-        public String getRevision() {
-            return version;
-        }
-
-        public long getLastModified() {
-            throw new UnsupportedOperationException();
-        }
-    }
-}
diff --git a/subprojects/core/src/main/groovy/org/gradle/api/internal/cache/BinaryStore.java b/subprojects/core/src/main/groovy/org/gradle/api/internal/cache/BinaryStore.java
new file mode 100644
index 0000000..fe3ca3d
--- /dev/null
+++ b/subprojects/core/src/main/groovy/org/gradle/api/internal/cache/BinaryStore.java
@@ -0,0 +1,41 @@
+/*
+ * Copyright 2013 the original author or authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.gradle.api.internal.cache;
+
+import java.io.DataInputStream;
+import java.io.DataOutputStream;
+import java.io.IOException;
+
+public interface BinaryStore {
+    void write(WriteAction write);
+    //done writing data, release any resources
+    BinaryData done();
+
+    public static interface WriteAction {
+        void write(DataOutputStream output) throws IOException;
+    }
+
+    public static interface ReadAction<T> {
+        T read(DataInputStream input) throws IOException;
+    }
+
+    public static interface BinaryData {
+        <T> T read(ReadAction<T> readAction);
+        //done reading data, release any resources
+        void done();
+    }
+}
diff --git a/subprojects/core/src/main/groovy/org/gradle/api/internal/cache/Store.java b/subprojects/core/src/main/groovy/org/gradle/api/internal/cache/Store.java
new file mode 100644
index 0000000..ddd0a5e
--- /dev/null
+++ b/subprojects/core/src/main/groovy/org/gradle/api/internal/cache/Store.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.cache;
+
+import org.gradle.internal.Factory;
+
+public interface Store<T> {
+    T load(Factory<T> createIfNotPresent);
+}
\ No newline at end of file
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
deleted file mode 100644
index a93221e..0000000
--- a/subprojects/core/src/main/groovy/org/gradle/api/internal/changedetection/CacheBackedFileSnapshotRepository.java
+++ /dev/null
@@ -1,44 +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.api.internal.changedetection;
-
-import org.gradle.cache.PersistentIndexedCache;
-
-public class CacheBackedFileSnapshotRepository implements FileSnapshotRepository {
-    private final PersistentIndexedCache<Object, Object> cache;
-
-    public CacheBackedFileSnapshotRepository(TaskArtifactStateCacheAccess cacheAccess) {
-        cache = cacheAccess.createCache("fileSnapshots", Object.class, Object.class);
-    }
-
-    public Long add(FileCollectionSnapshot snapshot) {
-        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) {
-        return (FileCollectionSnapshot) cache.get(id);
-    }
-
-    public void remove(Long id) {
-        cache.remove(id);
-    }
-}
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
deleted file mode 100644
index ac68218..0000000
--- a/subprojects/core/src/main/groovy/org/gradle/api/internal/changedetection/CacheBackedTaskHistoryRepository.java
+++ /dev/null
@@ -1,174 +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.api.internal.changedetection;
-
-import org.gradle.internal.Factory;
-import org.gradle.api.internal.TaskInternal;
-import org.gradle.messaging.serialize.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 TaskArtifactStateCacheAccess cacheAccess;
-    private final FileSnapshotRepository snapshotRepository;
-    private final PersistentIndexedCache<String, TaskHistory> taskHistoryCache;
-    private final DefaultSerializer<TaskHistory> serializer = new DefaultSerializer<TaskHistory>();
-
-    public CacheBackedTaskHistoryRepository(TaskArtifactStateCacheAccess cacheAccess, FileSnapshotRepository snapshotRepository) {
-        this.cacheAccess = cacheAccess;
-        this.snapshotRepository = snapshotRepository;
-        taskHistoryCache = cacheAccess.createCache("taskArtifacts", String.class, TaskHistory.class, serializer);
-    }
-
-    public History getHistory(final TaskInternal task) {
-        final TaskHistory history = loadHistory(task);
-        final LazyTaskExecution currentExecution = new LazyTaskExecution();
-        currentExecution.snapshotRepository = snapshotRepository;
-        currentExecution.cacheAccess = cacheAccess;
-        currentExecution.setOutputFiles(outputFiles(task));
-        final LazyTaskExecution previousExecution = findPreviousExecution(currentExecution, history);
-        if (previousExecution != null) {
-            previousExecution.snapshotRepository = snapshotRepository;
-            previousExecution.cacheAccess = cacheAccess;
-        }
-        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;
-        private transient TaskArtifactStateCacheAccess cacheAccess;
-
-        @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 = cacheAccess.useCache("fetch output files", new Factory<FileCollectionSnapshot>() {
-                    public FileCollectionSnapshot create() {
-                        return 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/CacheLockHandlingTaskExecuter.java b/subprojects/core/src/main/groovy/org/gradle/api/internal/changedetection/CacheLockHandlingTaskExecuter.java
deleted file mode 100644
index 9663558..0000000
--- a/subprojects/core/src/main/groovy/org/gradle/api/internal/changedetection/CacheLockHandlingTaskExecuter.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.api.internal.changedetection;
-
-import org.gradle.api.internal.TaskInternal;
-import org.gradle.api.internal.tasks.TaskExecuter;
-import org.gradle.api.internal.tasks.TaskStateInternal;
-
-public class CacheLockHandlingTaskExecuter implements TaskExecuter {
-    private final TaskExecuter executer;
-    private final TaskArtifactStateCacheAccess cacheAccess;
-
-    public CacheLockHandlingTaskExecuter(TaskExecuter executer, TaskArtifactStateCacheAccess cacheAccess) {
-        this.executer = executer;
-        this.cacheAccess = cacheAccess;
-    }
-
-    public void execute(final TaskInternal task, final TaskStateInternal state) {
-        cacheAccess.longRunningOperation(String.format("execute %s", task), new Runnable() {
-            public void run() {
-                executer.execute(task, state);
-            }
-        });
-    }
-}
diff --git a/subprojects/core/src/main/groovy/org/gradle/api/internal/changedetection/CachingHasher.java b/subprojects/core/src/main/groovy/org/gradle/api/internal/changedetection/CachingHasher.java
deleted file mode 100644
index f779d8d..0000000
--- a/subprojects/core/src/main/groovy/org/gradle/api/internal/changedetection/CachingHasher.java
+++ /dev/null
@@ -1,78 +0,0 @@
-/*
- * Copyright 2009 the original author or authors.
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- *      http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-package org.gradle.api.internal.changedetection;
-
-import org.gradle.cache.PersistentIndexedCache;
-import org.gradle.messaging.serialize.DataStreamBackedSerializer;
-
-import java.io.*;
-
-public class CachingHasher implements Hasher {
-    private final PersistentIndexedCache<File, FileInfo> cache;
-    private final Hasher hasher;
-    private long timestamp;
-
-    public CachingHasher(Hasher hasher, TaskArtifactStateCacheAccess cacheAccess) {
-        this.hasher = hasher;
-        cache = cacheAccess.createCache("fileHashes", File.class, FileInfo.class, new FileInfoSerializer());
-    }
-
-    public byte[] hash(File file) {
-        FileInfo info = cache.get(file);
-
-        long length = file.length();
-        timestamp = file.lastModified();
-        if (info != null && length == info.length && timestamp == info.timestamp) {
-            return info.hash;
-        }
-
-        byte[] hash = hasher.hash(file);
-        cache.put(file, new FileInfo(hash, length, timestamp));
-        return hash;
-    }
-
-    public static class FileInfo implements Serializable {
-        private final byte[] hash;
-        private final long timestamp;
-        private final long length;
-
-        public FileInfo(byte[] hash, long length, long timestamp) {
-            this.hash = hash;
-            this.length = length;
-            this.timestamp = timestamp;
-        }
-    }
-
-    private static class FileInfoSerializer extends DataStreamBackedSerializer<FileInfo> {
-        @Override
-        public FileInfo read(DataInput input) throws IOException {
-            int hashLength = input.readInt();
-            byte[] hash = new byte[hashLength];
-            input.readFully(hash);
-            long timestamp = input.readLong();
-            long length = input.readLong();
-            return new FileInfo(hash, length, timestamp);
-        }
-
-        @Override
-        public void write(DataOutput output, FileInfo value) throws IOException {
-            output.writeInt(value.hash.length);
-            output.write(value.hash);
-            output.writeLong(value.timestamp);
-            output.writeLong(value.length);
-        }
-    }
-}
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
deleted file mode 100644
index d739b3b..0000000
--- a/subprojects/core/src/main/groovy/org/gradle/api/internal/changedetection/CompositeUpToDateRule.java
+++ /dev/null
@@ -1,52 +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.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
deleted file mode 100644
index 50038d2..0000000
--- a/subprojects/core/src/main/groovy/org/gradle/api/internal/changedetection/DefaultFileCacheListener.java
+++ /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.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
deleted file mode 100755
index abd727b..0000000
--- a/subprojects/core/src/main/groovy/org/gradle/api/internal/changedetection/DefaultFileSnapshotter.java
+++ /dev/null
@@ -1,158 +0,0 @@
-/*
- * Copyright 2010 the original author or authors.
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- *      http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package org.gradle.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/DefaultHasher.java b/subprojects/core/src/main/groovy/org/gradle/api/internal/changedetection/DefaultHasher.java
deleted file mode 100644
index 23bfc30..0000000
--- a/subprojects/core/src/main/groovy/org/gradle/api/internal/changedetection/DefaultHasher.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.changedetection;
-
-import org.gradle.util.hash.HashUtil;
-
-import java.io.File;
-
-public class DefaultHasher implements Hasher {
-    public byte[] hash(File file) {
-        return HashUtil.createHash(file, "MD5").asByteArray();
-    }
-}
\ No newline at end of file
diff --git a/subprojects/core/src/main/groovy/org/gradle/api/internal/changedetection/DefaultTaskArtifactStateCacheAccess.java b/subprojects/core/src/main/groovy/org/gradle/api/internal/changedetection/DefaultTaskArtifactStateCacheAccess.java
deleted file mode 100644
index 310b159..0000000
--- a/subprojects/core/src/main/groovy/org/gradle/api/internal/changedetection/DefaultTaskArtifactStateCacheAccess.java
+++ /dev/null
@@ -1,85 +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.api.internal.changedetection;
-
-import org.gradle.internal.Factory;
-import org.gradle.api.invocation.Gradle;
-import org.gradle.cache.CacheRepository;
-import org.gradle.cache.PersistentCache;
-import org.gradle.cache.PersistentIndexedCache;
-import org.gradle.messaging.serialize.Serializer;
-import org.gradle.cache.internal.FileLockManager;
-import org.gradle.listener.LazyCreationProxy;
-
-import java.io.File;
-
-public class DefaultTaskArtifactStateCacheAccess implements TaskArtifactStateCacheAccess {
-    private final Gradle gradle;
-    private final CacheRepository cacheRepository;
-    private PersistentCache cache;
-
-    public DefaultTaskArtifactStateCacheAccess(Gradle gradle, CacheRepository cacheRepository) {
-        this.gradle = gradle;
-        this.cacheRepository = cacheRepository;
-    }
-
-    private PersistentCache getCache() {
-        if (cache == null) {
-            cache = cacheRepository
-                    .cache("taskArtifacts")
-                    .forObject(gradle)
-                    .withDisplayName("task artifact state cache")
-                    .withLockMode(FileLockManager.LockMode.Exclusive)
-                    .open();
-        }
-        return cache;
-    }
-
-    public <K, V> PersistentIndexedCache<K, V> createCache(final String cacheName, final Class<K> keyType, final Class<V> valueType) {
-        Factory<PersistentIndexedCache> factory = new Factory<PersistentIndexedCache>() {
-            public PersistentIndexedCache create() {
-                return getCache().createCache(cacheFile(cacheName), keyType, valueType);
-            }
-        };
-        return new LazyCreationProxy<PersistentIndexedCache>(PersistentIndexedCache.class, factory).getSource();
-    }
-
-    public <K, V> PersistentIndexedCache<K, V> createCache(final String cacheName, final Class<K> keyType, final Class<V> valueType, final Serializer<V> valueSerializer) {
-        Factory<PersistentIndexedCache> factory = new Factory<PersistentIndexedCache>() {
-            public PersistentIndexedCache create() {
-                return getCache().createCache(cacheFile(cacheName), keyType, valueSerializer);
-            }
-        };
-        return new LazyCreationProxy<PersistentIndexedCache>(PersistentIndexedCache.class, factory).getSource();
-
-    }
-
-    private File cacheFile(String cacheName) {
-        return new File(getCache().getBaseDir(), cacheName + ".bin");
-    }
-
-    public <T> T useCache(String operationDisplayName, Factory<? extends T> action) {
-        return getCache().useCache(operationDisplayName, action);
-    }
-
-    public void useCache(String operationDisplayName, Runnable action) {
-        getCache().useCache(operationDisplayName, action);
-    }
-
-    public void longRunningOperation(String operationDisplayName, Runnable action) {
-        getCache().longRunningOperation(operationDisplayName, action);
-    }
-}
\ No newline at end of file
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
deleted file mode 100644
index 458fb87..0000000
--- a/subprojects/core/src/main/groovy/org/gradle/api/internal/changedetection/DefaultTaskArtifactStateRepository.java
+++ /dev/null
@@ -1,172 +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.api.internal.changedetection;
-
-import org.gradle.api.file.FileCollection;
-import org.gradle.api.internal.TaskExecutionHistory;
-import org.gradle.api.internal.TaskInternal;
-import org.gradle.api.internal.file.collections.SimpleFileCollection;
-import org.gradle.api.logging.Logger;
-import org.gradle.api.logging.Logging;
-
-import java.util.ArrayList;
-import java.util.Formatter;
-import java.util.List;
-
-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 TaskHistoryRepository taskHistoryRepository;
-    private final UpToDateRule upToDateRule;
-
-    public DefaultTaskArtifactStateRepository(TaskHistoryRepository taskHistoryRepository, FileSnapshotter inputFilesSnapshotter, FileSnapshotter outputFilesSnapshotter) {
-        this.taskHistoryRepository = taskHistoryRepository;
-        upToDateRule = new CompositeUpToDateRule(
-                new TaskTypeChangedUpToDateRule(),
-                new InputPropertiesChangedUpToDateRule(),
-                new OutputFilesChangedUpToDateRule(outputFilesSnapshotter),
-                new InputFilesChangedUpToDateRule(inputFilesSnapshotter));
-    }
-
-    public TaskArtifactState getStateFor(final TaskInternal task) {
-        return new TaskArtifactStateImpl(task, taskHistoryRepository.getHistory(task));
-    }
-
-    private interface TaskExecutionState {
-        List<String> isUpToDate();
-
-        boolean snapshot();
-
-        FileCollection getPreviousOutputFiles();
-    }
-
-    private static class HistoricExecution implements TaskExecutionState {
-        private final TaskInternal task;
-        private final TaskExecution lastExecution;
-        private boolean upToDate;
-        private final UpToDateRule rule;
-        private TaskExecution thisExecution;
-        private UpToDateRule.TaskUpToDateState upToDateState;
-
-        public HistoricExecution(TaskInternal task, TaskHistoryRepository.History history, UpToDateRule rule) {
-            this.task = task;
-            this.lastExecution = history.getPreviousExecution();
-            this.thisExecution = history.getCurrentExecution();
-            this.rule = rule;
-        }
-
-        private void calcCurrentState() {
-            if (upToDateState != null) {
-                return;
-            }
-
-            // Calculate initial state - note this is potentially expensive
-            upToDateState = rule.create(task, lastExecution, thisExecution);
-        }
-
-        public FileCollection getPreviousOutputFiles() {
-            return lastExecution != null && lastExecution.getOutputFilesSnapshot() != null ? lastExecution.getOutputFilesSnapshot().getFiles() : new SimpleFileCollection();
-        }
-
-        public List<String> isUpToDate() {
-            calcCurrentState();
-
-            // Now determine if we're out of date
-            if (lastExecution == null) {
-                return singletonList(String.format("No history is available for %s.", task));
-            }
-
-            List<String> messages = new ArrayList<String>();
-            upToDateState.checkUpToDate(messages);
-
-            if (messages.isEmpty()) {
-                upToDate = true;
-            }
-            return messages;
-        }
-
-        public boolean snapshot() {
-            calcCurrentState();
-            
-            if (upToDate) {
-                return false;
-            }
-
-            upToDateState.snapshotAfterTask();
-            return true;
-        }
-    }
-
-    private class TaskArtifactStateImpl implements TaskArtifactState, TaskExecutionHistory {
-        private final TaskInternal task;
-        private final TaskHistoryRepository.History history;
-        private final TaskExecutionState execution;
-
-        public TaskArtifactStateImpl(TaskInternal task, TaskHistoryRepository.History history) {
-            this.task = task;
-            this.history = history;
-            execution = getExecution();
-        }
-
-        public boolean isUpToDate() {
-            List<String> messages = execution.isUpToDate();
-            if (messages == null || messages.isEmpty()) {
-                LOGGER.info("Skipping {} as it is up-to-date.", task);
-                return true;
-            }
-            if (LOGGER.isInfoEnabled()) {
-                Formatter formatter = new Formatter();
-                formatter.format("Executing %s due to:", task);
-                for (int i = 0; i < messages.size() && i < MAX_OUT_OF_DATE_MESSAGES; i++) {
-                    String message = messages.get(i);
-                    formatter.format("%n%s", message);
-                }
-                if (messages.size() > MAX_OUT_OF_DATE_MESSAGES) {
-                    formatter.format("%n%d more ...", messages.size() - MAX_OUT_OF_DATE_MESSAGES);
-                }
-                LOGGER.info(formatter.toString());
-            }
-            return false;
-        }
-
-        public FileCollection getOutputFiles() {
-            return execution.getPreviousOutputFiles();
-        }
-
-        public TaskExecutionHistory getExecutionHistory() {
-            return this;
-        }
-
-        public TaskExecutionState getExecution() {
-            return new HistoricExecution(task, history, upToDateRule);
-        }
-
-        public void afterTask() {
-            if (execution.snapshot()) {
-                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
deleted file mode 100644
index c23ccad..0000000
--- a/subprojects/core/src/main/groovy/org/gradle/api/internal/changedetection/FileCacheBroadcastTaskArtifactStateRepository.java
+++ /dev/null
@@ -1,63 +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.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
deleted file mode 100644
index 1906d55..0000000
--- a/subprojects/core/src/main/groovy/org/gradle/api/internal/changedetection/FileCacheListener.java
+++ /dev/null
@@ -1,35 +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.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/FileCollectionSnapshot.java b/subprojects/core/src/main/groovy/org/gradle/api/internal/changedetection/FileCollectionSnapshot.java
deleted file mode 100755
index b3f0cac..0000000
--- a/subprojects/core/src/main/groovy/org/gradle/api/internal/changedetection/FileCollectionSnapshot.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.changedetection;
-
-import org.gradle.api.file.FileCollection;
-import org.gradle.util.ChangeListener;
-
-import java.io.File;
-import java.io.Serializable;
-
-/**
- * An immutable snapshot of the contents of a collection of files.
- */
-public interface FileCollectionSnapshot extends Serializable {
-    void changesSince(FileCollectionSnapshot oldSnapshot, ChangeListener<File> listener);
-
-    Diff changesSince(FileCollectionSnapshot oldSnapshot);
-
-    FileCollection getFiles();
-
-    public interface Diff {
-        /**
-         * Applies this diff to the given snapshot. Adds any added or changed files in this diff to the given snapshot.
-         * Removes any removed files in this diff from the given snapshot.
-         *
-         * @param snapshot the snapshot to apply the changes to.
-         * @param listener the listener to notify of changes. The listener can veto a particular change.
-         * @return an updated copy of the provided snapshot
-         */
-        FileCollectionSnapshot applyTo(FileCollectionSnapshot snapshot, ChangeListener<Merge> listener);
-
-        /**
-         * Applies this diff to the given snapshot. Adds any added or changed files in this diff to the given snapshot.
-         * Removes any removed files in this diff from the given snapshot.
-         *
-         * @param snapshot the snapshot to apply the changes to.
-         * @return an updated copy of the provided snapshot
-         */
-        FileCollectionSnapshot applyTo(FileCollectionSnapshot snapshot);
-    }
-
-    public interface Merge {
-        void ignore();
-    }
-}
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
deleted file mode 100644
index b83e755..0000000
--- a/subprojects/core/src/main/groovy/org/gradle/api/internal/changedetection/FileSnapshotRepository.java
+++ /dev/null
@@ -1,24 +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.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
deleted file mode 100755
index fa65cfc..0000000
--- a/subprojects/core/src/main/groovy/org/gradle/api/internal/changedetection/FileSnapshotter.java
+++ /dev/null
@@ -1,35 +0,0 @@
-/*
- * Copyright 2010 the original author or authors.
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- *      http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-package org.gradle.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/Hasher.java b/subprojects/core/src/main/groovy/org/gradle/api/internal/changedetection/Hasher.java
deleted file mode 100644
index db4ac84..0000000
--- a/subprojects/core/src/main/groovy/org/gradle/api/internal/changedetection/Hasher.java
+++ /dev/null
@@ -1,22 +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.changedetection;
-
-import java.io.File;
-
-public interface Hasher {
-    byte[] hash(File file);
-}
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
deleted file mode 100644
index e03e6c3..0000000
--- a/subprojects/core/src/main/groovy/org/gradle/api/internal/changedetection/InMemoryIndexedCache.java
+++ /dev/null
@@ -1,65 +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.api.internal.changedetection;
-
-import org.gradle.cache.PersistentIndexedCache;
-import org.gradle.internal.UncheckedException;
-import org.gradle.messaging.serialize.Serializer;
-
-import java.io.ByteArrayInputStream;
-import java.io.ByteArrayOutputStream;
-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> {
-    private final Map<Object, byte[]> entries = new HashMap<Object, byte[]>();
-    private final Serializer<V> valueSerializer;
-
-    public InMemoryIndexedCache(Serializer<V> valueSerializer) {
-        this.valueSerializer = valueSerializer;
-    }
-
-    public V get(K key) {
-        byte[] serialised = entries.get(key);
-        if (serialised == null) {
-            return null;
-        }
-        try {
-            ByteArrayInputStream instr = new ByteArrayInputStream(serialised);
-            return valueSerializer.read(instr);
-        } catch (Exception e) {
-            throw UncheckedException.throwAsUncheckedException(e);
-        }
-    }
-
-    public void put(K key, V value) {
-        ByteArrayOutputStream outstr = new ByteArrayOutputStream();
-        try {
-            valueSerializer.write(outstr, value);
-        } catch (Exception e) {
-            throw UncheckedException.throwAsUncheckedException(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
deleted file mode 100644
index 7df4355..0000000
--- a/subprojects/core/src/main/groovy/org/gradle/api/internal/changedetection/InputFilesChangedUpToDateRule.java
+++ /dev/null
@@ -1,63 +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.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
deleted file mode 100644
index cbff546..0000000
--- a/subprojects/core/src/main/groovy/org/gradle/api/internal/changedetection/InputPropertiesChangedUpToDateRule.java
+++ /dev/null
@@ -1,55 +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.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/MapMergeChangeListener.java b/subprojects/core/src/main/groovy/org/gradle/api/internal/changedetection/MapMergeChangeListener.java
deleted file mode 100644
index 4c1e52e..0000000
--- a/subprojects/core/src/main/groovy/org/gradle/api/internal/changedetection/MapMergeChangeListener.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.changedetection;
-
-import org.gradle.util.ChangeListener;
-
-import java.util.Map;
-
-class MapMergeChangeListener<K, V> implements ChangeListener<Map.Entry<K, V>> {
-    private final ChangeListener<FileCollectionSnapshot.Merge> listener;
-    private final Map<K, V> newSnapshots;
-
-    public MapMergeChangeListener(ChangeListener<FileCollectionSnapshot.Merge> listener, Map<K, V> targetMap) {
-        this.listener = listener;
-        this.newSnapshots = targetMap;
-    }
-
-    public void added(Map.Entry<K, V> element) {
-        DefaultMerge merge = new DefaultMerge();
-        listener.added(merge);
-        if (!merge.isIgnore()) {
-            newSnapshots.put(element.getKey(), element.getValue());
-        }
-    }
-
-    public void removed(Map.Entry<K, V> element) {
-        DefaultMerge merge = new DefaultMerge();
-        listener.removed(merge);
-        if (!merge.isIgnore()) {
-            newSnapshots.remove(element.getKey());
-        }
-    }
-
-    public void changed(Map.Entry<K, V> element) {
-        DefaultMerge merge = new DefaultMerge();
-        listener.changed(merge);
-        if (!merge.isIgnore()) {
-            newSnapshots.put(element.getKey(), element.getValue());
-        }
-    }
-
-    private static class DefaultMerge implements FileCollectionSnapshot.Merge {
-        private boolean ignore;
-
-        public boolean isIgnore() {
-            return ignore;
-        }
-
-        public void ignore() {
-            ignore = true;
-        }
-    }
-}
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
deleted file mode 100644
index 57eca84..0000000
--- a/subprojects/core/src/main/groovy/org/gradle/api/internal/changedetection/OutputFilesChangedUpToDateRule.java
+++ /dev/null
@@ -1,85 +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.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
deleted file mode 100644
index 21dfeb3..0000000
--- a/subprojects/core/src/main/groovy/org/gradle/api/internal/changedetection/OutputFilesSnapshotter.java
+++ /dev/null
@@ -1,154 +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.changedetection;
-
-import org.gradle.api.file.FileCollection;
-import org.gradle.cache.PersistentIndexedCache;
-import org.gradle.internal.id.IdGenerator;
-import org.gradle.util.ChangeListener;
-import org.gradle.util.DiffUtil;
-import org.gradle.util.NoOpChangeListener;
-
-import java.io.File;
-import java.util.HashMap;
-import java.util.Map;
-
-/**
- * Takes a snapshot of the output files of a task. 2 parts to the algorithm:
- *
- * <ul>
- * <li>Collect the unique id for each output file and directory. The unique id is generated when we notice that
- * a file/directory has been created. The id is regenerated when the file/directory is deleted.</li>
- *
- * <li>Collect the hash of each output file and each file in each output directory.</li>
- * </ul>
- *
- */
-public class OutputFilesSnapshotter implements FileSnapshotter {
-    private final FileSnapshotter snapshotter;
-    private final IdGenerator<Long> idGenerator;
-    private final PersistentIndexedCache<String, Long> dirIdentiferCache;
-
-    public OutputFilesSnapshotter(FileSnapshotter snapshotter, IdGenerator<Long> idGenerator,
-                                  TaskArtifactStateCacheAccess cacheAccess) {
-        this.snapshotter = snapshotter;
-        this.idGenerator = idGenerator;
-        dirIdentiferCache = cacheAccess.createCache("outputFileStates", String.class, Long.class);
-    }
-
-    public FileCollectionSnapshot emptySnapshot() {
-        return new OutputFilesSnapshot(new HashMap<String, Long>(), snapshotter.emptySnapshot());
-    }
-
-    public FileCollectionSnapshot snapshot(FileCollection files) {
-        Map<String, Long> snapshotDirIds = new HashMap<String, Long>();
-        for (File file : files) {
-            Long dirId;
-            if (file.exists()) {
-                dirId = dirIdentiferCache.get(file.getAbsolutePath());
-                if (dirId == null) {
-                    dirId = idGenerator.generateId();
-                    dirIdentiferCache.put(file.getAbsolutePath(), dirId);
-                }
-            } else {
-                dirIdentiferCache.remove(file.getAbsolutePath());
-                dirId = null;
-            }
-            snapshotDirIds.put(file.getAbsolutePath(), dirId);
-        }
-        return new OutputFilesSnapshot(snapshotDirIds, snapshotter.snapshot(files));
-    }
-
-    private static class OutputFilesSnapshot implements FileCollectionSnapshot {
-        private final Map<String, Long> rootFileIds;
-        private final FileCollectionSnapshot filesSnapshot;
-
-        public OutputFilesSnapshot(Map<String, Long> rootFileIds, FileCollectionSnapshot filesSnapshot) {
-            this.rootFileIds = rootFileIds;
-            this.filesSnapshot = filesSnapshot;
-        }
-
-        public FileCollection getFiles() {
-            return filesSnapshot.getFiles();
-        }
-
-        public Diff changesSince(final FileCollectionSnapshot oldSnapshot) {
-            OutputFilesSnapshot other = (OutputFilesSnapshot) oldSnapshot;
-            return new OutputFilesDiff(rootFileIds, other.rootFileIds, filesSnapshot.changesSince(other.filesSnapshot));
-        }
-
-        public void changesSince(FileCollectionSnapshot oldSnapshot, final ChangeListener<File> listener) {
-            final OutputFilesSnapshot other = (OutputFilesSnapshot) oldSnapshot;
-            DiffUtil.diff(rootFileIds, other.rootFileIds, new ChangeListener<Map.Entry<String, Long>>() {
-                public void added(Map.Entry<String, Long> element) {
-                    listener.added(new File(element.getKey()));
-                }
-
-                public void removed(Map.Entry<String, Long> element) {
-                    listener.removed(new File(element.getKey()));
-                }
-
-                public void changed(Map.Entry<String, Long> element) {
-                    if (other.rootFileIds.get(element.getKey()) == null) {
-                        // Dir used to not exist, now does. Don't care
-                        return;
-                    }
-                    listener.changed(new File(element.getKey()));
-                }
-            });
-            filesSnapshot.changesSince(other.filesSnapshot, new ChangeListener<File>() {
-                public void added(File element) {
-                    // Ignore files added to output dirs which have been added since last time task executed
-                }
-
-                public void removed(File element) {
-                    listener.removed(element);
-                }
-
-                public void changed(File element) {
-                    listener.changed(element);
-                }
-            });
-        }
-    }
-
-    private static class OutputFilesDiff implements FileCollectionSnapshot.Diff {
-        private final Map<String, Long> newFileIds;
-        private final Map<String, Long> oldFileIds;
-        private final FileCollectionSnapshot.Diff filesDiff;
-
-        public OutputFilesDiff(Map<String, Long> newFileIds, Map<String, Long> oldFileIds,
-                               FileCollectionSnapshot.Diff filesDiff) {
-            this.newFileIds = newFileIds;
-            this.oldFileIds = oldFileIds;
-            this.filesDiff = filesDiff;
-        }
-
-        public FileCollectionSnapshot applyTo(FileCollectionSnapshot snapshot,
-                                              ChangeListener<FileCollectionSnapshot.Merge> listener) {
-            OutputFilesSnapshot other = (OutputFilesSnapshot) snapshot;
-            Map<String, Long> dirIds = new HashMap<String, Long>(other.rootFileIds);
-            DiffUtil.diff(newFileIds, oldFileIds, new MapMergeChangeListener<String, Long>(
-                    new NoOpChangeListener<FileCollectionSnapshot.Merge>(), dirIds));
-            return new OutputFilesSnapshot(newFileIds, filesDiff.applyTo(other.filesSnapshot, listener));
-        }
-
-        public FileCollectionSnapshot applyTo(FileCollectionSnapshot snapshot) {
-            return applyTo(snapshot, new NoOpChangeListener<FileCollectionSnapshot.Merge>());
-        }
-    }
-}
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
deleted file mode 100755
index 7b490be..0000000
--- a/subprojects/core/src/main/groovy/org/gradle/api/internal/changedetection/ShortCircuitTaskArtifactStateRepository.java
+++ /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.api.internal.changedetection;
-
-import org.apache.commons.lang.StringUtils;
-import org.gradle.StartParameter;
-import org.gradle.api.file.FileCollection;
-import org.gradle.api.internal.TaskExecutionHistory;
-import org.gradle.api.internal.TaskInternal;
-import org.gradle.api.logging.Logger;
-import org.gradle.api.logging.Logging;
-
-public class ShortCircuitTaskArtifactStateRepository implements TaskArtifactStateRepository {
-    private static final Logger LOGGER = Logging.getLogger(ShortCircuitTaskArtifactStateRepository.class);
-    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) {
-        if (task.getOutputs().getHasOutput()) {
-            return new ShortCircuitArtifactState(task, repository.getStateFor(task));
-        }
-        LOGGER.info(String.format("%s has not declared any outputs, assuming that it is out-of-date.", StringUtils.capitalize(task.toString())));
-        return new NoHistoryArtifactState();
-    }
-
-    private static class NoHistoryArtifactState implements TaskArtifactState, TaskExecutionHistory {
-        public boolean isUpToDate() {
-            return false;
-        }
-
-        public void beforeTask() {
-        }
-
-        public void afterTask() {
-        }
-
-        public void finished() {
-        }
-
-        public TaskExecutionHistory getExecutionHistory() {
-            return this;
-        }
-
-        public FileCollection getOutputFiles() {
-            throw new UnsupportedOperationException();
-        }
-    }
-
-    private class ShortCircuitArtifactState implements TaskArtifactState {
-        private final TaskInternal task;
-        private final TaskArtifactState state;
-
-        public ShortCircuitArtifactState(TaskInternal task, TaskArtifactState state) {
-            this.task = task;
-            this.state = state;
-        }
-
-        public boolean isUpToDate() {
-            return !startParameter.isRerunTasks() && 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 5fe4110..e095d26 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
@@ -16,6 +16,9 @@
 package org.gradle.api.internal.changedetection;
 
 import org.gradle.api.internal.TaskExecutionHistory;
+import org.gradle.api.tasks.incremental.IncrementalTaskInputs;
+
+import java.util.Collection;
 
 /**
  * Encapsulates the state of the task when its outputs were last generated.
@@ -23,11 +26,15 @@ import org.gradle.api.internal.TaskExecutionHistory;
 public interface TaskArtifactState {
     /**
      * Returns true if the task outputs were generated using the given task inputs.
+     *
+     * @param messages a collection to add messages which explain why the task is out-of-date.
      */
-    boolean isUpToDate();
+    boolean isUpToDate(Collection<String> messages);
+
+    IncrementalTaskInputs getInputChanges();
 
     /**
-     * Called before the task is to be executed. Note that {@link #isUpToDate()} may not necessarily have been called.
+     * Called before the task is to be executed. Note that {@link #isUpToDate(java.util.Collection)} may not necessarily have been called.
      */
     void beforeTask();
 
diff --git a/subprojects/core/src/main/groovy/org/gradle/api/internal/changedetection/TaskArtifactStateCacheAccess.java b/subprojects/core/src/main/groovy/org/gradle/api/internal/changedetection/TaskArtifactStateCacheAccess.java
deleted file mode 100644
index bf1525d..0000000
--- a/subprojects/core/src/main/groovy/org/gradle/api/internal/changedetection/TaskArtifactStateCacheAccess.java
+++ /dev/null
@@ -1,50 +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.api.internal.changedetection;
-
-import org.gradle.internal.Factory;
-import org.gradle.cache.PersistentIndexedCache;
-import org.gradle.messaging.serialize.Serializer;
-
-public interface TaskArtifactStateCacheAccess {
-    /**
-     * Performs some work against the cache. Acquires exclusive locks the appropriate resources, so that the given action is the only
-     * action to execute across all processes (including this one). Releases the locks and all resources at the end of the action.
-     *
-     * <p>This method is re-entrant, so that an action can call back into this method.</p>
-     */
-    <T> T useCache(String operationDisplayName, Factory<? extends T> action);
-
-    /**
-     * Performs some work against the cache. Acquires exclusive locks the appropriate resources, so that the given action is the only
-     * action to execute across all processes (including this one). Releases the locks and all resources at the end of the action.
-     *
-     * <p>This method is re-entrant, so that an action can call back into this method.</p>
-     */
-    void useCache(String operationDisplayName, Runnable action);
-
-    /**
-     * Performs some long running operation. Releases all locks while the operation is running, and reacquires the locks at the end of
-     * the long running operation.
-     *
-     * <p>This method is re-entrant, so that an action can call back into this method.</p>
-     */
-    void longRunningOperation(String operationDisplayName, Runnable action);
-
-    <K, V> PersistentIndexedCache createCache(String cacheName, Class<K> keyType, Class<V> valueType);
-
-    <K, V> PersistentIndexedCache<K, V> createCache(String cacheName, Class<K> keyType, Class<V> valueType, Serializer<V> valueSerializer);
-}
diff --git a/subprojects/core/src/main/groovy/org/gradle/api/internal/changedetection/TaskCacheLockHandlingBuildExecuter.java b/subprojects/core/src/main/groovy/org/gradle/api/internal/changedetection/TaskCacheLockHandlingBuildExecuter.java
deleted file mode 100644
index 3dfbf20..0000000
--- a/subprojects/core/src/main/groovy/org/gradle/api/internal/changedetection/TaskCacheLockHandlingBuildExecuter.java
+++ /dev/null
@@ -1,35 +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.api.internal.changedetection;
-
-import org.gradle.execution.BuildExecutionAction;
-import org.gradle.execution.BuildExecutionContext;
-
-public class TaskCacheLockHandlingBuildExecuter implements BuildExecutionAction {
-    private final TaskArtifactStateCacheAccess cacheAccess;
-
-    public TaskCacheLockHandlingBuildExecuter(TaskArtifactStateCacheAccess cacheAccess) {
-        this.cacheAccess = cacheAccess;
-    }
-
-    public void execute(final BuildExecutionContext context) {
-        cacheAccess.useCache("execute tasks", new Runnable(){
-            public void run() {
-                context.proceed();
-            }
-        });
-    }
-}
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
deleted file mode 100644
index 16aff96..0000000
--- a/subprojects/core/src/main/groovy/org/gradle/api/internal/changedetection/TaskExecution.java
+++ /dev/null
@@ -1,67 +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.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
deleted file mode 100644
index 9f71553..0000000
--- a/subprojects/core/src/main/groovy/org/gradle/api/internal/changedetection/TaskHistoryRepository.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.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
deleted file mode 100644
index c166808..0000000
--- a/subprojects/core/src/main/groovy/org/gradle/api/internal/changedetection/TaskTypeChangedUpToDateRule.java
+++ /dev/null
@@ -1,42 +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.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
deleted file mode 100644
index 2f47511..0000000
--- a/subprojects/core/src/main/groovy/org/gradle/api/internal/changedetection/UpToDateRule.java
+++ /dev/null
@@ -1,48 +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.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/changedetection/changes/ChangesOnlyIncrementalTaskInputs.java b/subprojects/core/src/main/groovy/org/gradle/api/internal/changedetection/changes/ChangesOnlyIncrementalTaskInputs.java
new file mode 100644
index 0000000..590f179
--- /dev/null
+++ b/subprojects/core/src/main/groovy/org/gradle/api/internal/changedetection/changes/ChangesOnlyIncrementalTaskInputs.java
@@ -0,0 +1,57 @@
+/*
+ * Copyright 2013 the original author or authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY 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.changes;
+
+import org.gradle.api.Action;
+import org.gradle.api.internal.changedetection.rules.TaskStateChange;
+import org.gradle.api.internal.changedetection.rules.TaskStateChanges;
+import org.gradle.api.tasks.incremental.InputFileDetails;
+
+import java.util.ArrayList;
+import java.util.List;
+
+public class ChangesOnlyIncrementalTaskInputs extends StatefulIncrementalTaskInputs {
+    private final TaskStateChanges inputFilesState;
+    private List<InputFileDetails> removedFiles = new ArrayList<InputFileDetails>();
+
+    public ChangesOnlyIncrementalTaskInputs(TaskStateChanges inputFilesState) {
+        this.inputFilesState = inputFilesState;
+    }
+
+    public boolean isIncremental() {
+        return true;
+    }
+
+    @Override
+    protected void doOutOfDate(final Action<? super InputFileDetails> outOfDateAction) {
+        for (TaskStateChange change : inputFilesState) {
+            InputFileDetails fileChange = (InputFileDetails) change;
+            if (fileChange.isRemoved()) {
+                removedFiles.add(fileChange);
+            } else {
+                outOfDateAction.execute(fileChange);
+            }
+        }
+    }
+
+    @Override
+    protected void doRemoved(Action<? super InputFileDetails> removedAction) {
+        for (InputFileDetails removedFile : removedFiles) {
+            removedAction.execute(removedFile);
+        }
+    }
+}
diff --git a/subprojects/core/src/main/groovy/org/gradle/api/internal/changedetection/changes/DefaultTaskArtifactStateRepository.java b/subprojects/core/src/main/groovy/org/gradle/api/internal/changedetection/changes/DefaultTaskArtifactStateRepository.java
new file mode 100644
index 0000000..d7daee1
--- /dev/null
+++ b/subprojects/core/src/main/groovy/org/gradle/api/internal/changedetection/changes/DefaultTaskArtifactStateRepository.java
@@ -0,0 +1,133 @@
+/*
+ * Copyright 2011 the original author or authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY 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.changes;
+
+import org.gradle.api.file.FileCollection;
+import org.gradle.api.internal.TaskExecutionHistory;
+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.changedetection.rules.TaskStateChange;
+import org.gradle.api.internal.changedetection.rules.TaskStateChanges;
+import org.gradle.api.internal.changedetection.rules.TaskUpToDateState;
+import org.gradle.api.internal.changedetection.state.FileSnapshotter;
+import org.gradle.api.internal.changedetection.state.TaskExecution;
+import org.gradle.api.internal.changedetection.state.TaskHistoryRepository;
+import org.gradle.api.internal.file.collections.SimpleFileCollection;
+import org.gradle.api.tasks.incremental.IncrementalTaskInputs;
+import org.gradle.internal.reflect.Instantiator;
+
+import java.util.ArrayList;
+import java.util.Collection;
+import java.util.List;
+
+public class DefaultTaskArtifactStateRepository implements TaskArtifactStateRepository {
+
+    private final TaskHistoryRepository taskHistoryRepository;
+    private final FileSnapshotter outputFilesSnapshotter;
+    private final FileSnapshotter inputFilesSnapshotter;
+    private final Instantiator instantiator;
+
+    public DefaultTaskArtifactStateRepository(TaskHistoryRepository taskHistoryRepository, Instantiator instantiator,
+                                              FileSnapshotter outputFilesSnapshotter, FileSnapshotter inputFilesSnapshotter) {
+        this.taskHistoryRepository = taskHistoryRepository;
+        this.instantiator = instantiator;
+        this.outputFilesSnapshotter = outputFilesSnapshotter;
+        this.inputFilesSnapshotter = inputFilesSnapshotter;
+    }
+
+    public TaskArtifactState getStateFor(final TaskInternal task) {
+        return new TaskArtifactStateImpl(task, taskHistoryRepository.getHistory(task));
+    }
+
+    private class TaskArtifactStateImpl implements TaskArtifactState, TaskExecutionHistory {
+        private final TaskInternal task;
+        private final TaskHistoryRepository.History history;
+        private boolean upToDate;
+        private TaskUpToDateState states;
+
+        public TaskArtifactStateImpl(TaskInternal task, TaskHistoryRepository.History history) {
+            this.task = task;
+            this.history = history;
+        }
+
+        public boolean isUpToDate(Collection<String> messages) {
+            final List<String> reasons = getChangeMessages(getStates().getAllTaskChanges());
+            messages.addAll(reasons);
+            if (reasons.isEmpty()) {
+                upToDate = true;
+                return true;
+            }
+            return false;
+        }
+
+        private List<String> getChangeMessages(TaskStateChanges stateChanges) {
+            final List<String> messages = new ArrayList<String>();
+            for (TaskStateChange stateChange : stateChanges) {
+                messages.add(stateChange.getMessage());
+            }
+            return messages;
+        }
+
+        public IncrementalTaskInputs getInputChanges() {
+            assert !upToDate : "Should not be here if the task is up-to-date";
+
+            if (canPerformIncrementalBuild()) {
+                return instantiator.newInstance(ChangesOnlyIncrementalTaskInputs.class, getStates().getInputFilesChanges());
+            }
+            return instantiator.newInstance(RebuildIncrementalTaskInputs.class, task);
+        }
+
+        private boolean canPerformIncrementalBuild() {
+            final List<String> messages = getChangeMessages(getStates().getRebuildChanges());
+            return messages.isEmpty();
+        }
+
+        public FileCollection getOutputFiles() {
+            TaskExecution lastExecution = history.getPreviousExecution();
+            return lastExecution != null && lastExecution.getOutputFilesSnapshot() != null ? lastExecution.getOutputFilesSnapshot().getFiles() : new SimpleFileCollection();
+        }
+
+        public TaskExecutionHistory getExecutionHistory() {
+            return this;
+        }
+
+        public void beforeTask() {
+        }
+
+        public void afterTask() {
+            if (upToDate) {
+                return;
+            }
+
+            getStates().getAllTaskChanges().snapshotAfterTask();
+            history.update();
+        }
+
+        public void finished() {
+        }
+
+        private TaskUpToDateState getStates() {
+            if (states == null) {
+                // Calculate initial state - note this is potentially expensive
+                states = new TaskUpToDateState(task, history, outputFilesSnapshotter, inputFilesSnapshotter);
+            }
+            return states;
+        }
+    }
+
+}
diff --git a/subprojects/core/src/main/groovy/org/gradle/api/internal/changedetection/changes/NoHistoryArtifactState.java b/subprojects/core/src/main/groovy/org/gradle/api/internal/changedetection/changes/NoHistoryArtifactState.java
new file mode 100644
index 0000000..3577a34
--- /dev/null
+++ b/subprojects/core/src/main/groovy/org/gradle/api/internal/changedetection/changes/NoHistoryArtifactState.java
@@ -0,0 +1,52 @@
+/*
+ * Copyright 2013 the original author or authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY 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.changes;
+
+import org.gradle.api.file.FileCollection;
+import org.gradle.api.internal.TaskExecutionHistory;
+import org.gradle.api.internal.changedetection.TaskArtifactState;
+import org.gradle.api.tasks.incremental.IncrementalTaskInputs;
+
+import java.util.Collection;
+
+class NoHistoryArtifactState implements TaskArtifactState, TaskExecutionHistory {
+    public boolean isUpToDate(Collection<String> messages) {
+        messages.add("Task has not declared any outputs.");
+        return false;
+    }
+
+    public IncrementalTaskInputs getInputChanges() {
+        throw new UnsupportedOperationException();
+    }
+
+    public TaskExecutionHistory getExecutionHistory() {
+        return this;
+    }
+
+    public void beforeTask() {
+    }
+
+    public void afterTask() {
+    }
+
+    public void finished() {
+    }
+
+    public FileCollection getOutputFiles() {
+        throw new UnsupportedOperationException();
+    }
+}
diff --git a/subprojects/core/src/main/groovy/org/gradle/api/internal/changedetection/changes/RebuildIncrementalTaskInputs.java b/subprojects/core/src/main/groovy/org/gradle/api/internal/changedetection/changes/RebuildIncrementalTaskInputs.java
new file mode 100644
index 0000000..e227766
--- /dev/null
+++ b/subprojects/core/src/main/groovy/org/gradle/api/internal/changedetection/changes/RebuildIncrementalTaskInputs.java
@@ -0,0 +1,73 @@
+/*
+ * Copyright 2013 the original author or authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY 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.changes;
+
+import org.gradle.api.Action;
+import org.gradle.api.Task;
+import org.gradle.api.tasks.incremental.InputFileDetails;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+import java.io.File;
+
+public class RebuildIncrementalTaskInputs extends StatefulIncrementalTaskInputs {
+    private static final Logger LOGGER = LoggerFactory.getLogger(RebuildIncrementalTaskInputs.class);
+
+    private final Task task;
+
+    public RebuildIncrementalTaskInputs(Task task) {
+        LOGGER.info("All input files are considered out-of-date for incremental {}.", task);
+        this.task = task;
+    }
+
+    public boolean isIncremental() {
+        return false;
+    }
+
+    public void doOutOfDate(Action<? super InputFileDetails> outOfDateAction) {
+        for (File file : task.getInputs().getFiles()) {
+            outOfDateAction.execute(new RebuildInputFile(file));
+        }
+    }
+
+    public void doRemoved(Action<? super InputFileDetails> removedAction) {
+    }
+
+    private static class RebuildInputFile implements InputFileDetails {
+        private final File file;
+
+        private RebuildInputFile(File file) {
+            this.file = file;
+        }
+
+        public File getFile() {
+            return file;
+        }
+
+        public boolean isAdded() {
+            return false;
+        }
+
+        public boolean isModified() {
+            return false;
+        }
+
+        public boolean isRemoved() {
+            return false;
+        }
+    }
+}
diff --git a/subprojects/core/src/main/groovy/org/gradle/api/internal/changedetection/changes/ShortCircuitTaskArtifactStateRepository.java b/subprojects/core/src/main/groovy/org/gradle/api/internal/changedetection/changes/ShortCircuitTaskArtifactStateRepository.java
new file mode 100755
index 0000000..9bb3ad8
--- /dev/null
+++ b/subprojects/core/src/main/groovy/org/gradle/api/internal/changedetection/changes/ShortCircuitTaskArtifactStateRepository.java
@@ -0,0 +1,96 @@
+/*
+ * Copyright 2011 the original author or authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY 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.changes;
+
+import org.gradle.StartParameter;
+import org.gradle.api.internal.TaskExecutionHistory;
+import org.gradle.api.internal.TaskInternal;
+import org.gradle.api.internal.changedetection.TaskArtifactState;
+import org.gradle.api.internal.changedetection.TaskArtifactStateRepository;
+import org.gradle.api.tasks.incremental.IncrementalTaskInputs;
+import org.gradle.internal.reflect.Instantiator;
+
+import java.util.Collection;
+
+public class ShortCircuitTaskArtifactStateRepository implements TaskArtifactStateRepository {
+
+    private final StartParameter startParameter;
+    private final TaskArtifactStateRepository repository;
+    private final Instantiator instantiator;
+
+    public ShortCircuitTaskArtifactStateRepository(StartParameter startParameter, Instantiator instantiator, TaskArtifactStateRepository repository) {
+        this.startParameter = startParameter;
+        this.instantiator = instantiator;
+        this.repository = repository;
+    }
+
+    public TaskArtifactState getStateFor(final TaskInternal task) {
+
+        if (!task.getOutputs().getHasOutput()) { // Only false if no declared outputs AND no Task.upToDateWhen spec. We force to true for incremental tasks.
+            return new NoHistoryArtifactState();
+        }
+
+        final TaskArtifactState state = repository.getStateFor(task);
+
+        if (startParameter.isRerunTasks()) {
+            return new RerunTaskArtifactState(state, task, "Executed with '--rerun-tasks'.");
+        }
+
+        if (!task.getOutputs().getUpToDateSpec().isSatisfiedBy(task)) {
+            return new RerunTaskArtifactState(state, task, "Task.upToDateWhen is false.");
+        }
+
+        return state;
+    }
+
+    private class RerunTaskArtifactState implements TaskArtifactState {
+        private final TaskArtifactState delegate;
+        private final TaskInternal task;
+        private final String reason;
+
+        private RerunTaskArtifactState(TaskArtifactState delegate, TaskInternal task, String reason) {
+            this.delegate = delegate;
+            this.task = task;
+            this.reason = reason;
+        }
+
+        public boolean isUpToDate(Collection<String> messages) {
+            messages.add(reason);
+            return false;
+        }
+
+        public IncrementalTaskInputs getInputChanges() {
+            return instantiator.newInstance(RebuildIncrementalTaskInputs.class, task);
+        }
+
+        public TaskExecutionHistory getExecutionHistory() {
+            return delegate.getExecutionHistory();
+        }
+
+        public void beforeTask() {
+            delegate.beforeTask();
+        }
+
+        public void afterTask() {
+            delegate.afterTask();
+        }
+
+        public void finished() {
+            delegate.finished();
+        }
+    }
+
+}
diff --git a/subprojects/core/src/main/groovy/org/gradle/api/internal/changedetection/changes/StatefulIncrementalTaskInputs.java b/subprojects/core/src/main/groovy/org/gradle/api/internal/changedetection/changes/StatefulIncrementalTaskInputs.java
new file mode 100644
index 0000000..35bca19
--- /dev/null
+++ b/subprojects/core/src/main/groovy/org/gradle/api/internal/changedetection/changes/StatefulIncrementalTaskInputs.java
@@ -0,0 +1,49 @@
+/*
+ * Copyright 2013 the original author or authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY 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.changes;
+
+import org.gradle.api.Action;
+import org.gradle.api.tasks.incremental.IncrementalTaskInputs;
+import org.gradle.api.tasks.incremental.InputFileDetails;
+
+abstract class StatefulIncrementalTaskInputs implements IncrementalTaskInputs {
+    private boolean outOfDateProcessed;
+    private boolean removedProcessed;
+
+    public void outOfDate(final Action<? super InputFileDetails> outOfDateAction) {
+        if (outOfDateProcessed) {
+            throw new IllegalStateException("Cannot process outOfDate files multiple times");
+        }
+        doOutOfDate(outOfDateAction);
+        outOfDateProcessed = true;
+    }
+
+    protected abstract void doOutOfDate(Action<? super InputFileDetails> outOfDateAction);
+
+    public void removed(Action<? super InputFileDetails> removedAction) {
+        if (!outOfDateProcessed) {
+            throw new IllegalStateException("Must first process outOfDate files before processing removed files");
+        }
+        if (removedProcessed) {
+            throw new IllegalStateException("Cannot process removed files multiple times");
+        }
+        doRemoved(removedAction);
+        removedProcessed = true;
+    }
+
+    protected abstract void doRemoved(Action<? super InputFileDetails> removedAction);
+}
diff --git a/subprojects/core/src/main/groovy/org/gradle/api/internal/changedetection/rules/CachingTaskStateChanges.java b/subprojects/core/src/main/groovy/org/gradle/api/internal/changedetection/rules/CachingTaskStateChanges.java
new file mode 100644
index 0000000..cda5490
--- /dev/null
+++ b/subprojects/core/src/main/groovy/org/gradle/api/internal/changedetection/rules/CachingTaskStateChanges.java
@@ -0,0 +1,86 @@
+/*
+ * Copyright 2013 the original author or authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY 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.rules;
+
+import com.google.common.collect.AbstractIterator;
+
+import java.util.ArrayList;
+import java.util.Iterator;
+import java.util.List;
+
+/**
+ * An implementation that will cache a number of results of iteration, and make them available for subsequent iterations.
+ * This allows a single underlying iterator to be used for multiple iterations, up til the first iteration that exceeds the cache size.
+ * When the cache is overrun, it is cleared so that any subsequent iteration will require a fresh delegate iterator.
+ */
+public class CachingTaskStateChanges implements TaskStateChanges {
+    private final TaskStateChanges delegate;
+    private final List<TaskStateChange> cache = new ArrayList<TaskStateChange>();
+    private final int maxCachedChanges;
+    private Iterator<TaskStateChange> delegateIterator;
+    boolean overrun;
+
+    public CachingTaskStateChanges(int maxCachedChanges, TaskStateChanges delegate) {
+        this.maxCachedChanges = maxCachedChanges;
+        this.delegate = delegate;
+    }
+
+    public Iterator<TaskStateChange> iterator() {
+        if (delegateIterator == null || overrun) {
+            reset();
+        }
+
+        return new AbstractIterator<TaskStateChange>() {
+            final Iterator<TaskStateChange> cacheIterator = new ArrayList<TaskStateChange>(cache).iterator();
+
+            @Override
+            protected TaskStateChange computeNext() {
+                if (cacheIterator.hasNext()) {
+                    return cacheIterator.next();
+                }
+                if (delegateIterator.hasNext()) {
+                    TaskStateChange next = delegateIterator.next();
+                    maybeCache(next);
+                    return next;
+                }
+                return endOfData();
+            }
+        };
+    }
+
+    private void maybeCache(TaskStateChange next) {
+        if (overrun) {
+            return;
+        }
+
+        if (cache.size() < maxCachedChanges) {
+            cache.add(next);
+        } else {
+            overrun = true;
+        }
+    }
+
+    private void reset() {
+        cache.clear();
+        delegateIterator = delegate.iterator();
+        overrun = false;
+    }
+
+    public void snapshotAfterTask() {
+        delegate.snapshotAfterTask();
+    }
+}
diff --git a/subprojects/core/src/main/groovy/org/gradle/api/internal/changedetection/rules/ChangeType.java b/subprojects/core/src/main/groovy/org/gradle/api/internal/changedetection/rules/ChangeType.java
new file mode 100644
index 0000000..3f59e2c
--- /dev/null
+++ b/subprojects/core/src/main/groovy/org/gradle/api/internal/changedetection/rules/ChangeType.java
@@ -0,0 +1,33 @@
+/*
+ * Copyright 2013 the original author or authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY 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.rules;
+
+enum ChangeType {
+    ADDED("has been added"),
+    MODIFIED("has changed"),
+    REMOVED("has been removed");
+
+    private final String description;
+
+    private ChangeType(String description) {
+        this.description = description;
+    }
+
+    public String describe() {
+        return description;
+    }
+}
diff --git a/subprojects/core/src/main/groovy/org/gradle/api/internal/changedetection/rules/DescriptiveChange.java b/subprojects/core/src/main/groovy/org/gradle/api/internal/changedetection/rules/DescriptiveChange.java
new file mode 100644
index 0000000..321e392
--- /dev/null
+++ b/subprojects/core/src/main/groovy/org/gradle/api/internal/changedetection/rules/DescriptiveChange.java
@@ -0,0 +1,29 @@
+/*
+ * Copyright 2013 the original author or authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY 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.rules;
+
+class DescriptiveChange implements TaskStateChange {
+    private final String message;
+
+    public DescriptiveChange(String message, Object... params) {
+        this.message = String.format(message, params);
+    }
+
+    public String getMessage() {
+        return message;
+    }
+}
diff --git a/subprojects/core/src/main/groovy/org/gradle/api/internal/changedetection/rules/FileChange.java b/subprojects/core/src/main/groovy/org/gradle/api/internal/changedetection/rules/FileChange.java
new file mode 100644
index 0000000..f73a71e
--- /dev/null
+++ b/subprojects/core/src/main/groovy/org/gradle/api/internal/changedetection/rules/FileChange.java
@@ -0,0 +1,60 @@
+/*
+ * Copyright 2013 the original author or authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY 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.rules;
+
+import java.io.File;
+
+abstract class FileChange implements TaskStateChange {
+    private final String path;
+    private final ChangeType change;
+
+    public FileChange(String path, ChangeType change) {
+        this.path = path;
+        this.change = change;
+    }
+
+    public String getMessage() {
+        return String.format("%s file %s %s.", getFileType(), path, change.describe());
+    }
+
+    @Override
+    public String toString() {
+        return getMessage();
+    }
+
+    protected abstract String getFileType();
+
+    public String getPath() {
+        return path;
+    }
+
+    public File getFile() {
+        return new File(path);
+    }
+
+    public boolean isAdded() {
+        return change == ChangeType.ADDED;
+    }
+
+    public boolean isModified() {
+        return change == ChangeType.MODIFIED;
+    }
+
+    public boolean isRemoved() {
+        return change == ChangeType.REMOVED;
+    }
+}
diff --git a/subprojects/core/src/main/groovy/org/gradle/api/internal/changedetection/rules/InputFileChange.java b/subprojects/core/src/main/groovy/org/gradle/api/internal/changedetection/rules/InputFileChange.java
new file mode 100644
index 0000000..a67677a
--- /dev/null
+++ b/subprojects/core/src/main/groovy/org/gradle/api/internal/changedetection/rules/InputFileChange.java
@@ -0,0 +1,31 @@
+/*
+ * Copyright 2013 the original author or authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY 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.rules;
+
+import org.gradle.api.tasks.incremental.InputFileDetails;
+
+class InputFileChange extends FileChange implements InputFileDetails {
+
+    public InputFileChange(String path, ChangeType change) {
+        super(path, change);
+    }
+
+    @Override
+    protected String getFileType() {
+        return "Input";
+    }
+}
diff --git a/subprojects/core/src/main/groovy/org/gradle/api/internal/changedetection/rules/InputFilesStateChangeRule.java b/subprojects/core/src/main/groovy/org/gradle/api/internal/changedetection/rules/InputFilesStateChangeRule.java
new file mode 100644
index 0000000..bdb7620
--- /dev/null
+++ b/subprojects/core/src/main/groovy/org/gradle/api/internal/changedetection/rules/InputFilesStateChangeRule.java
@@ -0,0 +1,77 @@
+/*
+ * Copyright 2013 the original author or authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY 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.rules;
+
+import com.google.common.collect.AbstractIterator;
+import org.gradle.api.internal.TaskInternal;
+import org.gradle.api.internal.changedetection.state.FileCollectionSnapshot;
+import org.gradle.api.internal.changedetection.state.FileSnapshotter;
+import org.gradle.api.internal.changedetection.state.TaskExecution;
+import org.gradle.util.ChangeListener;
+
+import java.util.Collections;
+import java.util.Iterator;
+
+/**
+ * A rule which detects changes in the input files of a task.
+ */
+class InputFilesStateChangeRule {
+    public static TaskStateChanges create(final TaskInternal task, final TaskExecution previousExecution, final TaskExecution currentExecution, final FileSnapshotter inputFilesSnapshotter) {
+        final FileCollectionSnapshot inputFilesSnapshot = inputFilesSnapshotter.snapshot(task.getInputs().getFiles());
+
+        return new TaskStateChanges() {
+
+            public Iterator<TaskStateChange> iterator() {
+                if (previousExecution.getInputFilesSnapshot() == null) {
+                    return Collections.<TaskStateChange>singleton(new DescriptiveChange("Input file history is not available.")).iterator();
+                }
+
+                return new AbstractIterator<TaskStateChange>() {
+                    final FileCollectionSnapshot.ChangeIterator<String> changeIterator = inputFilesSnapshot.iterateChangesSince(previousExecution.getInputFilesSnapshot());
+                    final ChangeListenerAdapter listenerAdapter = new ChangeListenerAdapter();
+
+                    @Override
+                    protected TaskStateChange computeNext() {
+                        if (changeIterator.next(listenerAdapter)) {
+                            return listenerAdapter.lastChange;
+                        }
+                        return endOfData();
+                    }
+                };
+            }
+
+            public void snapshotAfterTask() {
+                currentExecution.setInputFilesSnapshot(inputFilesSnapshot);
+            }
+        };
+    }
+
+    private static class ChangeListenerAdapter implements ChangeListener<String> {
+        public InputFileChange lastChange;
+
+        public void added(String fileName) {
+            lastChange = new InputFileChange(fileName, ChangeType.ADDED);
+        }
+
+        public void removed(String fileName) {
+            lastChange = new InputFileChange(fileName, ChangeType.REMOVED);
+        }
+
+        public void changed(String fileName) {
+            lastChange = new InputFileChange(fileName, ChangeType.MODIFIED);
+        }
+    }
+}
diff --git a/subprojects/core/src/main/groovy/org/gradle/api/internal/changedetection/rules/InputPropertiesStateChangeRule.java b/subprojects/core/src/main/groovy/org/gradle/api/internal/changedetection/rules/InputPropertiesStateChangeRule.java
new file mode 100644
index 0000000..57d0c38
--- /dev/null
+++ b/subprojects/core/src/main/groovy/org/gradle/api/internal/changedetection/rules/InputPropertiesStateChangeRule.java
@@ -0,0 +1,54 @@
+/*
+ * Copyright 2013 the original author or authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY 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.rules;
+
+import org.gradle.api.internal.TaskInternal;
+import org.gradle.api.internal.changedetection.state.TaskExecution;
+import org.gradle.util.ChangeListener;
+import org.gradle.util.DiffUtil;
+
+import java.util.HashMap;
+import java.util.List;
+import java.util.Map;
+
+/**
+ * A rule which detects changes in the input properties of a task.
+ */
+class InputPropertiesStateChangeRule {
+    public static TaskStateChanges 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 SimpleTaskStateChanges() {
+            @Override
+            protected void addAllChanges(final List<TaskStateChange> changes) {
+                DiffUtil.diff(properties, previousExecution.getInputProperties(), new ChangeListener<Map.Entry<String, Object>>() {
+                    public void added(Map.Entry<String, Object> element) {
+                        changes.add(new DescriptiveChange("Input property '%s' has been added for %s", element.getKey(), task));
+                    }
+
+                    public void removed(Map.Entry<String, Object> element) {
+                        changes.add(new DescriptiveChange("Input property '%s' has been removed for %s", element.getKey(), task));
+                    }
+
+                    public void changed(Map.Entry<String, Object> element) {
+                        changes.add(new DescriptiveChange("Value of input property '%s' has changed for %s", element.getKey(), task));
+                    }
+                });
+            }
+        };
+    }
+}
diff --git a/subprojects/core/src/main/groovy/org/gradle/api/internal/changedetection/rules/NoHistoryStateChangeRule.java b/subprojects/core/src/main/groovy/org/gradle/api/internal/changedetection/rules/NoHistoryStateChangeRule.java
new file mode 100644
index 0000000..0f27534
--- /dev/null
+++ b/subprojects/core/src/main/groovy/org/gradle/api/internal/changedetection/rules/NoHistoryStateChangeRule.java
@@ -0,0 +1,35 @@
+/*
+ * Copyright 2013 the original author or authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY 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.rules;
+
+import org.gradle.api.internal.TaskInternal;
+import org.gradle.api.internal.changedetection.state.TaskExecution;
+
+import java.util.List;
+
+class NoHistoryStateChangeRule {
+    public static TaskStateChanges create(final TaskInternal task, final TaskExecution previousExecution) {
+        return new SimpleTaskStateChanges() {
+            @Override
+            protected void addAllChanges(List<TaskStateChange> changes) {
+                if (previousExecution == null) {
+                    changes.add(new DescriptiveChange("No history is available."));
+                }
+            }
+        };
+    }
+}
diff --git a/subprojects/core/src/main/groovy/org/gradle/api/internal/changedetection/rules/OutputFileChange.java b/subprojects/core/src/main/groovy/org/gradle/api/internal/changedetection/rules/OutputFileChange.java
new file mode 100644
index 0000000..ab4fbad
--- /dev/null
+++ b/subprojects/core/src/main/groovy/org/gradle/api/internal/changedetection/rules/OutputFileChange.java
@@ -0,0 +1,29 @@
+/*
+ * Copyright 2013 the original author or authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY 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.rules;
+
+class OutputFileChange extends FileChange {
+
+    public OutputFileChange(String path, ChangeType change) {
+        super(path, change);
+    }
+
+    @Override
+    protected String getFileType() {
+        return "Output";
+    }
+}
diff --git a/subprojects/core/src/main/groovy/org/gradle/api/internal/changedetection/rules/OutputFilesStateChangeRule.java b/subprojects/core/src/main/groovy/org/gradle/api/internal/changedetection/rules/OutputFilesStateChangeRule.java
new file mode 100644
index 0000000..b3b91cd
--- /dev/null
+++ b/subprojects/core/src/main/groovy/org/gradle/api/internal/changedetection/rules/OutputFilesStateChangeRule.java
@@ -0,0 +1,100 @@
+/*
+ * Copyright 2013 the original author or authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY 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.rules;
+
+import com.google.common.collect.AbstractIterator;
+import org.gradle.api.internal.TaskInternal;
+import org.gradle.api.internal.changedetection.state.FileCollectionSnapshot;
+import org.gradle.api.internal.changedetection.state.FileSnapshotter;
+import org.gradle.api.internal.changedetection.state.TaskExecution;
+import org.gradle.util.ChangeListener;
+
+import java.util.Collections;
+import java.util.Iterator;
+
+/**
+ * A rule which detects changes in output files.
+ */
+class OutputFilesStateChangeRule {
+
+    public static TaskStateChanges create(final TaskInternal task, final TaskExecution previousExecution, final TaskExecution currentExecution, final FileSnapshotter outputFilesSnapshotter) {
+        final FileCollectionSnapshot outputFilesBefore = outputFilesSnapshotter.snapshot(task.getOutputs().getFiles());
+
+        return new TaskStateChanges() {
+
+            public Iterator<TaskStateChange> iterator() {
+                if (previousExecution.getOutputFilesSnapshot() == null) {
+                    return Collections.<TaskStateChange>singleton(new DescriptiveChange("Output file history is not available.")).iterator();
+                }
+
+                return new AbstractIterator<TaskStateChange>() {
+                    final FileCollectionSnapshot.ChangeIterator<String> changeIterator = outputFilesBefore.iterateChangesSince(previousExecution.getOutputFilesSnapshot());
+                    final ChangeListenerAdapter listenerAdapter = new ChangeListenerAdapter();
+
+                    @Override
+                    protected TaskStateChange computeNext() {
+                        if (changeIterator.next(listenerAdapter)) {
+                            return listenerAdapter.lastChange;
+                        }
+                        return endOfData();
+                    }
+                };
+            }
+
+            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));
+            }
+        };
+    }
+
+    private static class ChangeListenerAdapter implements ChangeListener<String> {
+        public OutputFileChange lastChange;
+
+        public void added(String fileName) {
+            lastChange = new OutputFileChange(fileName, ChangeType.ADDED);
+        }
+
+        public void removed(String fileName) {
+            lastChange = new OutputFileChange(fileName, ChangeType.REMOVED);
+        }
+
+        public void changed(String fileName) {
+            lastChange = new OutputFileChange(fileName, ChangeType.MODIFIED);
+        }
+    }
+}
diff --git a/subprojects/core/src/main/groovy/org/gradle/api/internal/changedetection/rules/SimpleTaskStateChanges.java b/subprojects/core/src/main/groovy/org/gradle/api/internal/changedetection/rules/SimpleTaskStateChanges.java
new file mode 100644
index 0000000..a2488b1
--- /dev/null
+++ b/subprojects/core/src/main/groovy/org/gradle/api/internal/changedetection/rules/SimpleTaskStateChanges.java
@@ -0,0 +1,38 @@
+/*
+ * Copyright 2013 the original author or authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY 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.rules;
+
+import java.util.ArrayList;
+import java.util.Iterator;
+import java.util.List;
+
+abstract class SimpleTaskStateChanges implements TaskStateChanges {
+    private List<TaskStateChange> changes;
+
+    public Iterator<TaskStateChange> iterator() {
+        if (changes == null) {
+            changes = new ArrayList<TaskStateChange>();
+            addAllChanges(changes);
+        }
+        return changes.iterator();
+    }
+
+    protected abstract void addAllChanges(List<TaskStateChange> changes);
+
+    public void snapshotAfterTask() {
+    }
+}
diff --git a/subprojects/core/src/main/groovy/org/gradle/api/internal/changedetection/rules/SummaryTaskStateChanges.java b/subprojects/core/src/main/groovy/org/gradle/api/internal/changedetection/rules/SummaryTaskStateChanges.java
new file mode 100644
index 0000000..ba85d4b
--- /dev/null
+++ b/subprojects/core/src/main/groovy/org/gradle/api/internal/changedetection/rules/SummaryTaskStateChanges.java
@@ -0,0 +1,76 @@
+/*
+ * Copyright 2013 the original author or authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY 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.rules;
+
+import com.google.common.collect.AbstractIterator;
+
+import java.util.Arrays;
+import java.util.Iterator;
+import java.util.List;
+
+class SummaryTaskStateChanges implements TaskStateChanges {
+    private final int maxReportedChanges;
+    private final List<TaskStateChanges> sources;
+
+
+    public SummaryTaskStateChanges(int maxReportedChanges, TaskStateChanges... sources) {
+        this.maxReportedChanges = maxReportedChanges;
+        this.sources = Arrays.asList(sources);
+    }
+
+    /**
+     * Provides an efficient summary of the changes, without doing too much unnecessary work.
+     * - Will only emit changes of a single type (from a single delegate change set)
+     * - Will return no more than the specified maximum of number of changes
+     */
+    public Iterator<TaskStateChange> iterator() {
+
+        return new AbstractIterator<TaskStateChange>() {
+            Iterator<TaskStateChange> changes;
+            int count;
+
+            @Override
+            protected TaskStateChange computeNext() {
+                if (changes == null) {
+                    changes = firstDirtyIterator();
+                }
+
+                if (count < maxReportedChanges && changes != null && changes.hasNext()) {
+                    count++;
+                    return changes.next();
+                }
+                return endOfData();
+            }
+        };
+    }
+
+    private Iterator<TaskStateChange> firstDirtyIterator() {
+        for (TaskStateChanges source : sources) {
+            Iterator<TaskStateChange> sourceIterator = source.iterator();
+            if (sourceIterator.hasNext()) {
+                return sourceIterator;
+            }
+        }
+        return null;
+    }
+
+    public void snapshotAfterTask() {
+        for (TaskStateChanges state : sources) {
+            state.snapshotAfterTask();
+        }
+    }
+}
diff --git a/subprojects/core/src/main/groovy/org/gradle/api/internal/changedetection/rules/TaskStateChange.java b/subprojects/core/src/main/groovy/org/gradle/api/internal/changedetection/rules/TaskStateChange.java
new file mode 100644
index 0000000..af2f2f4
--- /dev/null
+++ b/subprojects/core/src/main/groovy/org/gradle/api/internal/changedetection/rules/TaskStateChange.java
@@ -0,0 +1,21 @@
+/*
+ * Copyright 2013 the original author or authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY 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.rules;
+
+public interface TaskStateChange {
+    String getMessage();
+}
diff --git a/subprojects/core/src/main/groovy/org/gradle/api/internal/changedetection/rules/TaskStateChanges.java b/subprojects/core/src/main/groovy/org/gradle/api/internal/changedetection/rules/TaskStateChanges.java
new file mode 100644
index 0000000..d75a122
--- /dev/null
+++ b/subprojects/core/src/main/groovy/org/gradle/api/internal/changedetection/rules/TaskStateChanges.java
@@ -0,0 +1,25 @@
+/*
+ * Copyright 2013 the original author or authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY 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.rules;
+
+public interface TaskStateChanges extends Iterable<TaskStateChange> {
+    /**
+     * 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 org.gradle.api.internal.changedetection.state.TaskExecution} object for the current execution.
+     */
+    void snapshotAfterTask();
+}
diff --git a/subprojects/core/src/main/groovy/org/gradle/api/internal/changedetection/rules/TaskTypeStateChangeRule.java b/subprojects/core/src/main/groovy/org/gradle/api/internal/changedetection/rules/TaskTypeStateChangeRule.java
new file mode 100644
index 0000000..182ce28
--- /dev/null
+++ b/subprojects/core/src/main/groovy/org/gradle/api/internal/changedetection/rules/TaskTypeStateChangeRule.java
@@ -0,0 +1,43 @@
+/*
+ * Copyright 2013 the original author or authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY 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.rules;
+
+import org.apache.commons.lang.StringUtils;
+import org.gradle.api.internal.TaskInternal;
+import org.gradle.api.internal.changedetection.state.TaskExecution;
+
+import java.util.List;
+
+/**
+ * A rule which detects changes in the task implementation class.
+ */
+class TaskTypeStateChangeRule {
+    public static TaskStateChanges create(final TaskInternal task, final TaskExecution previousExecution, final TaskExecution currentExecution) {
+        final String taskClass = task.getClass().getName();
+        currentExecution.setTaskClass(taskClass);
+
+        return new SimpleTaskStateChanges() {
+            @Override
+            protected void addAllChanges(List<TaskStateChange> changes) {
+                if (!taskClass.equals(previousExecution.getTaskClass())) {
+                    changes.add(new DescriptiveChange("%s has changed type from '%s' to '%s'.",
+                            StringUtils.capitalize(task.toString()), previousExecution.getTaskClass(), task.getClass().getName()));
+                }
+            }
+        };
+    }
+
+}
diff --git a/subprojects/core/src/main/groovy/org/gradle/api/internal/changedetection/rules/TaskUpToDateState.java b/subprojects/core/src/main/groovy/org/gradle/api/internal/changedetection/rules/TaskUpToDateState.java
new file mode 100644
index 0000000..15346e5
--- /dev/null
+++ b/subprojects/core/src/main/groovy/org/gradle/api/internal/changedetection/rules/TaskUpToDateState.java
@@ -0,0 +1,66 @@
+/*
+ * Copyright 2013 the original author or authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY 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.rules;
+
+import org.gradle.api.internal.TaskInternal;
+import org.gradle.api.internal.changedetection.state.FileSnapshotter;
+import org.gradle.api.internal.changedetection.state.TaskExecution;
+import org.gradle.api.internal.changedetection.state.TaskHistoryRepository;
+
+/**
+ * Represents the complete changes in a tasks state
+ */
+public class TaskUpToDateState {
+    private static final int MAX_OUT_OF_DATE_MESSAGES = 3;
+
+    private TaskStateChanges noHistoryState;
+    private TaskStateChanges inputFilesState;
+    private TaskStateChanges inputPropertiesState;
+    private TaskStateChanges taskTypeState;
+    private TaskStateChanges outputFilesState;
+    private SummaryTaskStateChanges allTaskChanges;
+    private SummaryTaskStateChanges rebuildChanges;
+
+    public TaskUpToDateState(TaskInternal task, TaskHistoryRepository.History history, FileSnapshotter outputFilesSnapshotter, FileSnapshotter inputFilesSnapshotter) {
+        TaskExecution thisExecution = history.getCurrentExecution();
+        TaskExecution lastExecution = history.getPreviousExecution();
+
+        noHistoryState = NoHistoryStateChangeRule.create(task, lastExecution);
+        taskTypeState = TaskTypeStateChangeRule.create(task, lastExecution, thisExecution);
+        inputPropertiesState = InputPropertiesStateChangeRule.create(task, lastExecution, thisExecution);
+        outputFilesState = caching(OutputFilesStateChangeRule.create(task, lastExecution, thisExecution, outputFilesSnapshotter));
+        inputFilesState = caching(InputFilesStateChangeRule.create(task, lastExecution, thisExecution, inputFilesSnapshotter));
+        allTaskChanges = new SummaryTaskStateChanges(MAX_OUT_OF_DATE_MESSAGES, noHistoryState, taskTypeState, inputPropertiesState, outputFilesState, inputFilesState);
+        rebuildChanges = new SummaryTaskStateChanges(1, noHistoryState, taskTypeState, inputPropertiesState, outputFilesState);
+    }
+
+    private TaskStateChanges caching(TaskStateChanges wrapped) {
+        return new CachingTaskStateChanges(MAX_OUT_OF_DATE_MESSAGES, wrapped);
+    }
+
+    public TaskStateChanges getInputFilesChanges() {
+        return inputFilesState;
+    }
+
+    public TaskStateChanges getAllTaskChanges() {
+        return allTaskChanges;
+    }
+
+    public TaskStateChanges getRebuildChanges() {
+        return rebuildChanges;
+    }
+}
diff --git a/subprojects/core/src/main/groovy/org/gradle/api/internal/changedetection/state/CacheBackedFileSnapshotRepository.java b/subprojects/core/src/main/groovy/org/gradle/api/internal/changedetection/state/CacheBackedFileSnapshotRepository.java
new file mode 100644
index 0000000..fa95085
--- /dev/null
+++ b/subprojects/core/src/main/groovy/org/gradle/api/internal/changedetection/state/CacheBackedFileSnapshotRepository.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.api.internal.changedetection.state;
+
+import org.gradle.cache.PersistentIndexedCache;
+import org.gradle.internal.id.IdGenerator;
+import org.gradle.internal.id.RandomLongIdGenerator;
+
+public class CacheBackedFileSnapshotRepository implements FileSnapshotRepository {
+    private final PersistentIndexedCache<Long, FileCollectionSnapshot> cache;
+    private IdGenerator<Long> idGenerator = new RandomLongIdGenerator();
+
+    public CacheBackedFileSnapshotRepository(TaskArtifactStateCacheAccess cacheAccess, IdGenerator<Long> idGenerator) {
+        this.idGenerator = idGenerator;
+        cache = cacheAccess.createCache("fileSnapshots", Long.class, FileCollectionSnapshot.class, new FileSnapshotSerializer());
+    }
+
+    public Long add(FileCollectionSnapshot snapshot) {
+        Long id = idGenerator.generateId();
+        cache.put(id, snapshot);
+        return id;
+    }
+
+    public FileCollectionSnapshot get(Long id) {
+        return cache.get(id);
+    }
+
+    public void remove(Long id) {
+        cache.remove(id);
+    }
+}
diff --git a/subprojects/core/src/main/groovy/org/gradle/api/internal/changedetection/state/CacheBackedTaskHistoryRepository.java b/subprojects/core/src/main/groovy/org/gradle/api/internal/changedetection/state/CacheBackedTaskHistoryRepository.java
new file mode 100644
index 0000000..167b064
--- /dev/null
+++ b/subprojects/core/src/main/groovy/org/gradle/api/internal/changedetection/state/CacheBackedTaskHistoryRepository.java
@@ -0,0 +1,273 @@
+/*
+ * Copyright 2011 the original author or authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY 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.state;
+
+import org.gradle.api.internal.TaskInternal;
+import org.gradle.cache.PersistentIndexedCache;
+import org.gradle.internal.Factory;
+import org.gradle.messaging.serialize.*;
+
+import java.io.ByteArrayInputStream;
+import java.io.ByteArrayOutputStream;
+import java.io.File;
+import java.util.*;
+
+public class CacheBackedTaskHistoryRepository implements TaskHistoryRepository {
+    private final TaskArtifactStateCacheAccess cacheAccess;
+    private final FileSnapshotRepository snapshotRepository;
+    private final PersistentIndexedCache<String, TaskHistory> taskHistoryCache;
+    private final TaskHistorySerializer serializer = new TaskHistorySerializer();
+
+    public CacheBackedTaskHistoryRepository(TaskArtifactStateCacheAccess cacheAccess, FileSnapshotRepository snapshotRepository) {
+        this.cacheAccess = cacheAccess;
+        this.snapshotRepository = snapshotRepository;
+        taskHistoryCache = cacheAccess.createCache("taskArtifacts", String.class, TaskHistory.class, serializer);
+    }
+
+    public History getHistory(final TaskInternal task) {
+        final TaskHistory history = loadHistory(task);
+        final LazyTaskExecution currentExecution = new LazyTaskExecution();
+        currentExecution.snapshotRepository = snapshotRepository;
+        currentExecution.cacheAccess = cacheAccess;
+        currentExecution.setOutputFiles(outputFiles(task));
+        final LazyTaskExecution previousExecution = findPreviousExecution(currentExecution, history);
+        if (previousExecution != null) {
+            previousExecution.snapshotRepository = snapshotRepository;
+            previousExecution.cacheAccess = cacheAccess;
+        }
+        history.configurations.add(0, currentExecution);
+
+        return new History() {
+            public TaskExecution getPreviousExecution() {
+                return previousExecution;
+            }
+
+            public TaskExecution getCurrentExecution() {
+                return currentExecution;
+            }
+
+            public void update() {
+                cacheAccess.useCache("Update task history", new Runnable() {
+                    public void run() {
+                        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(final TaskInternal task) {
+        return cacheAccess.useCache("Load task history", new Factory<TaskHistory>() {
+            public TaskHistory create() {
+                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 TaskHistorySerializer implements Serializer<TaskHistory> {
+
+        private ClassLoader classLoader;
+
+        public TaskHistory read(Decoder decoder) throws Exception {
+            byte executions = decoder.readByte();
+            TaskHistory history = new TaskHistory();
+            LazyTaskExecution.TaskHistorySerializer executionSerializer = new LazyTaskExecution.TaskHistorySerializer(classLoader);
+            for (int i = 0; i < executions; i++) {
+                LazyTaskExecution exec = executionSerializer.read(decoder);
+                history.configurations.add(exec);
+            }
+            return history;
+        }
+
+        public void write(Encoder encoder, TaskHistory value) throws Exception {
+            int size = value.configurations.size();
+            encoder.writeByte((byte) size);
+            LazyTaskExecution.TaskHistorySerializer executionSerializer = new LazyTaskExecution.TaskHistorySerializer(classLoader);
+            for (LazyTaskExecution execution : value.configurations) {
+                executionSerializer.write(encoder, execution);
+            }
+        }
+
+        public ClassLoader getClassLoader() {
+            return classLoader;
+        }
+
+        public void setClassLoader(ClassLoader classLoader) {
+            this.classLoader = classLoader;
+        }
+    }
+
+    private static class TaskHistory {
+        private static final int MAX_HISTORY_ENTRIES = 3;
+        private final List<LazyTaskExecution> configurations = new ArrayList<LazyTaskExecution>();
+    }
+
+    //TODO SF extract & unit test
+    private static class LazyTaskExecution extends TaskExecution {
+        private Long inputFilesSnapshotId;
+        private Long outputFilesSnapshotId;
+        private transient FileSnapshotRepository snapshotRepository;
+        private transient FileCollectionSnapshot inputFilesSnapshot;
+        private transient FileCollectionSnapshot outputFilesSnapshot;
+        private transient TaskArtifactStateCacheAccess cacheAccess;
+
+        @Override
+        public FileCollectionSnapshot getInputFilesSnapshot() {
+            if (inputFilesSnapshot == null) {
+                inputFilesSnapshot = cacheAccess.useCache("fetch input files", new Factory<FileCollectionSnapshot>() {
+                    public FileCollectionSnapshot create() {
+                        return 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 = cacheAccess.useCache("fetch output files", new Factory<FileCollectionSnapshot>() {
+                    public FileCollectionSnapshot create() {
+                        return snapshotRepository.get(outputFilesSnapshotId);
+                    }
+                });
+            }
+            return outputFilesSnapshot;
+        }
+
+        @Override
+        public void setOutputFilesSnapshot(FileCollectionSnapshot outputFilesSnapshot) {
+            this.outputFilesSnapshot = outputFilesSnapshot;
+            outputFilesSnapshotId = null;
+        }
+
+        static class TaskHistorySerializer implements Serializer<LazyTaskExecution> {
+            private ClassLoader classLoader;
+
+            public TaskHistorySerializer(ClassLoader classLoader) {
+                this.classLoader = classLoader;
+            }
+
+            public LazyTaskExecution read(Decoder decoder) throws Exception {
+                LazyTaskExecution execution = new LazyTaskExecution();
+                execution.inputFilesSnapshotId = decoder.readLong();
+                execution.outputFilesSnapshotId = decoder.readLong();
+                execution.setTaskClass(decoder.readString());
+                int outputFiles = decoder.readInt();
+                Set<String> files = new HashSet<String>();
+                for (int j = 0; j < outputFiles; j++) {
+                    files.add(decoder.readString());
+                }
+                execution.setOutputFiles(files);
+
+                int inputProperties = decoder.readInt();
+                if (inputProperties > 0) {
+                    byte[] serializedMap = new byte[inputProperties];
+                    decoder.readBytes(serializedMap);
+                    DefaultSerializer<Map> defaultSerializer = new DefaultSerializer<Map>(classLoader);
+                    Map<String, Object> map = defaultSerializer.read(new InputStreamBackedDecoder(new ByteArrayInputStream(serializedMap)));
+                    execution.setInputProperties(map);
+                } else {
+                    execution.setInputProperties(new HashMap<String, Object>());
+                }
+                return execution;
+            }
+
+            public void write(Encoder encoder, LazyTaskExecution execution) throws Exception {
+                encoder.writeLong(execution.inputFilesSnapshotId);
+                encoder.writeLong(execution.outputFilesSnapshotId);
+                encoder.writeString(execution.getTaskClass());
+                encoder.writeInt(execution.getOutputFiles().size());
+                for (String outputFile : execution.getOutputFiles()) {
+                    encoder.writeString(outputFile);
+                }
+                if (execution.getInputProperties() == null) {
+                    encoder.writeInt(0);
+                } else {
+                    DefaultSerializer<Map> defaultSerializer = new DefaultSerializer<Map>(classLoader);
+                    ByteArrayOutputStream outputStream = new ByteArrayOutputStream();
+                    OutputStreamBackedEncoder propsEncoder = new OutputStreamBackedEncoder(outputStream);
+                    defaultSerializer.write(propsEncoder, execution.getInputProperties());
+                    propsEncoder.flush();
+                    byte[] serializedMap = outputStream.toByteArray();
+                    encoder.writeInt(serializedMap.length);
+                    encoder.writeBytes(serializedMap);
+                }
+            }
+        }
+    }
+}
diff --git a/subprojects/core/src/main/groovy/org/gradle/api/internal/changedetection/state/CachingHasher.java b/subprojects/core/src/main/groovy/org/gradle/api/internal/changedetection/state/CachingHasher.java
new file mode 100644
index 0000000..f51b2e7
--- /dev/null
+++ b/subprojects/core/src/main/groovy/org/gradle/api/internal/changedetection/state/CachingHasher.java
@@ -0,0 +1,76 @@
+/*
+ * 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.changedetection.state;
+
+import org.gradle.cache.PersistentIndexedCache;
+import org.gradle.messaging.serialize.Decoder;
+import org.gradle.messaging.serialize.Encoder;
+import org.gradle.messaging.serialize.Serializer;
+
+import java.io.File;
+import java.io.Serializable;
+
+public class CachingHasher implements Hasher {
+    private final PersistentIndexedCache<File, FileInfo> cache;
+    private final Hasher hasher;
+    private long timestamp;
+
+    public CachingHasher(Hasher hasher, TaskArtifactStateCacheAccess cacheAccess) {
+        this.hasher = hasher;
+        cache = cacheAccess.createCache("fileHashes", File.class, FileInfo.class, new FileInfoSerializer());
+    }
+
+    public byte[] hash(File file) {
+        FileInfo info = cache.get(file);
+
+        long length = file.length();
+        timestamp = file.lastModified();
+        if (info != null && length == info.length && timestamp == info.timestamp) {
+            return info.hash;
+        }
+
+        byte[] hash = hasher.hash(file);
+        cache.put(file, new FileInfo(hash, length, timestamp));
+        return hash;
+    }
+
+    public static class FileInfo implements Serializable {
+        private final byte[] hash;
+        private final long timestamp;
+        private final long length;
+
+        public FileInfo(byte[] hash, long length, long timestamp) {
+            this.hash = hash;
+            this.length = length;
+            this.timestamp = timestamp;
+        }
+    }
+
+    private static class FileInfoSerializer implements Serializer<FileInfo> {
+        public FileInfo read(Decoder decoder) throws Exception {
+            byte[] hash = decoder.readBinary();
+            long timestamp = decoder.readLong();
+            long length = decoder.readLong();
+            return new FileInfo(hash, length, timestamp);
+        }
+
+        public void write(Encoder encoder, FileInfo value) throws Exception {
+            encoder.writeBinary(value.hash);
+            encoder.writeLong(value.timestamp);
+            encoder.writeLong(value.length);
+        }
+    }
+}
diff --git a/subprojects/core/src/main/groovy/org/gradle/api/internal/changedetection/state/DefaultFileSnapshotter.java b/subprojects/core/src/main/groovy/org/gradle/api/internal/changedetection/state/DefaultFileSnapshotter.java
new file mode 100755
index 0000000..d4c6131
--- /dev/null
+++ b/subprojects/core/src/main/groovy/org/gradle/api/internal/changedetection/state/DefaultFileSnapshotter.java
@@ -0,0 +1,185 @@
+/*
+ * Copyright 2010 the original author or authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY 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.state;
+
+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.math.BigInteger;
+import java.util.*;
+
+public class DefaultFileSnapshotter implements FileSnapshotter {
+    private final Hasher hasher;
+    private TaskArtifactStateCacheAccess cacheAccess;
+
+    public DefaultFileSnapshotter(Hasher hasher, TaskArtifactStateCacheAccess cacheAccess) {
+        this.hasher = hasher;
+        this.cacheAccess = cacheAccess;
+    }
+
+    public FileCollectionSnapshot emptySnapshot() {
+        return new FileCollectionSnapshotImpl(new HashMap<String, FileSnapshot>());
+    }
+
+    public FileCollectionSnapshot snapshot(FileCollection sourceFiles) {
+        final Map<String, FileSnapshot> snapshots = new HashMap<String, FileSnapshot>();
+        final Set<File> theFiles = sourceFiles.getAsFileTree().getFiles();
+        cacheAccess.useCache("Create file snapshot", new Runnable() {
+            public void run() {
+                for (File file : theFiles) {
+                    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);
+    }
+
+    static interface FileSnapshot {
+        boolean isUpToDate(FileSnapshot snapshot);
+    }
+
+    static class FileHashSnapshot implements FileSnapshot {
+        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);
+        }
+    }
+
+    static class DirSnapshot implements FileSnapshot {
+        public boolean isUpToDate(FileSnapshot snapshot) {
+            return snapshot instanceof DirSnapshot;
+        }
+    }
+
+    static class MissingFileSnapshot implements FileSnapshot {
+        public boolean isUpToDate(FileSnapshot snapshot) {
+            return snapshot instanceof MissingFileSnapshot;
+        }
+    }
+
+    static class FileCollectionSnapshotImpl implements FileCollectionSnapshot {
+        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 ChangeIterator<String> iterateChangesSince(FileCollectionSnapshot oldSnapshot) {
+            FileCollectionSnapshotImpl other = (FileCollectionSnapshotImpl) oldSnapshot;
+            final Map<String, FileSnapshot> otherSnapshots = new HashMap<String, FileSnapshot>(other.snapshots);
+            final Iterator<String> currentFiles = snapshots.keySet().iterator();
+
+            return new ChangeIterator<String>() {
+                private Iterator<String> removedFiles;
+
+                public boolean next(ChangeListener<String> listener) {
+                    while (currentFiles.hasNext()) {
+                        String currentFile = currentFiles.next();
+                        FileSnapshot otherFile = otherSnapshots.remove(currentFile);
+
+                        if (otherFile == null) {
+                            listener.added(currentFile);
+                            return true;
+                        } else if (!snapshots.get(currentFile).isUpToDate(otherFile)) {
+                            listener.changed(currentFile);
+                            return true;
+                        }
+                    }
+
+                    // Create a single iterator to use for all of the removed files
+                    if (removedFiles == null) {
+                        removedFiles = otherSnapshots.keySet().iterator();
+                    }
+
+                    if (removedFiles.hasNext()) {
+                        listener.removed(removedFiles.next());
+                        return true;
+                    }
+
+                    return false;
+                }
+            };
+        }
+
+        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);
+                }
+            };
+        }
+
+        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);
+            }
+        }
+
+    }
+}
diff --git a/subprojects/core/src/main/groovy/org/gradle/api/internal/changedetection/state/DefaultFileSnapshotterSerializer.java b/subprojects/core/src/main/groovy/org/gradle/api/internal/changedetection/state/DefaultFileSnapshotterSerializer.java
new file mode 100644
index 0000000..777810b
--- /dev/null
+++ b/subprojects/core/src/main/groovy/org/gradle/api/internal/changedetection/state/DefaultFileSnapshotterSerializer.java
@@ -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.changedetection.state;
+
+import org.gradle.messaging.serialize.Decoder;
+import org.gradle.messaging.serialize.Encoder;
+import org.gradle.messaging.serialize.Serializer;
+
+import java.util.HashMap;
+import java.util.Map;
+
+class DefaultFileSnapshotterSerializer implements Serializer<FileCollectionSnapshot> {
+    public FileCollectionSnapshot read(Decoder decoder) throws Exception {
+        Map<String, DefaultFileSnapshotter.FileSnapshot> snapshots = new HashMap<String, DefaultFileSnapshotter.FileSnapshot>();
+        DefaultFileSnapshotter.FileCollectionSnapshotImpl snapshot = new DefaultFileSnapshotter.FileCollectionSnapshotImpl(snapshots);
+        int snapshotsCount = decoder.readInt();
+        for (int i = 0; i < snapshotsCount; i++) {
+            String key = decoder.readString();
+            byte fileSnapshotKind = decoder.readByte();
+            if (fileSnapshotKind == 1) {
+                snapshots.put(key, new DefaultFileSnapshotter.DirSnapshot());
+            } else if (fileSnapshotKind == 2) {
+                snapshots.put(key, new DefaultFileSnapshotter.MissingFileSnapshot());
+            } else if (fileSnapshotKind == 3) {
+                byte hashSize = decoder.readByte();
+                byte[] hash = new byte[hashSize];
+                decoder.readBytes(hash);
+                snapshots.put(key, new DefaultFileSnapshotter.FileHashSnapshot(hash));
+            } else {
+                throw new RuntimeException("Unable to read serialized file collection snapshot. Unrecognized value found in the data stream.");
+            }
+        }
+        return snapshot;
+    }
+
+    public void write(Encoder encoder, FileCollectionSnapshot value) throws Exception {
+        DefaultFileSnapshotter.FileCollectionSnapshotImpl cached = (DefaultFileSnapshotter.FileCollectionSnapshotImpl) value;
+        encoder.writeInt(cached.snapshots.size());
+        for (String key : cached.snapshots.keySet()) {
+            encoder.writeString(key);
+            DefaultFileSnapshotter.FileSnapshot fileSnapshot = cached.snapshots.get(key);
+            if (fileSnapshot instanceof DefaultFileSnapshotter.DirSnapshot) {
+                encoder.writeByte((byte) 1);
+            } else if (fileSnapshot instanceof DefaultFileSnapshotter.MissingFileSnapshot) {
+                encoder.writeByte((byte) 2);
+            } else if (fileSnapshot instanceof DefaultFileSnapshotter.FileHashSnapshot) {
+                encoder.writeByte((byte) 3);
+                byte[] hash = ((DefaultFileSnapshotter.FileHashSnapshot) fileSnapshot).hash;
+                encoder.writeByte((byte) hash.length);
+                encoder.writeBytes(hash);
+            }
+        }
+    }
+}
diff --git a/subprojects/core/src/main/groovy/org/gradle/api/internal/changedetection/state/DefaultHasher.java b/subprojects/core/src/main/groovy/org/gradle/api/internal/changedetection/state/DefaultHasher.java
new file mode 100644
index 0000000..a9c9c57
--- /dev/null
+++ b/subprojects/core/src/main/groovy/org/gradle/api/internal/changedetection/state/DefaultHasher.java
@@ -0,0 +1,26 @@
+/*
+ * Copyright 2009 the original author or authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.gradle.api.internal.changedetection.state;
+
+import org.gradle.util.hash.HashUtil;
+
+import java.io.File;
+
+public class DefaultHasher implements Hasher {
+    public byte[] hash(File file) {
+        return HashUtil.createHash(file, "MD5").asByteArray();
+    }
+}
\ No newline at end of file
diff --git a/subprojects/core/src/main/groovy/org/gradle/api/internal/changedetection/state/DefaultTaskArtifactStateCacheAccess.java b/subprojects/core/src/main/groovy/org/gradle/api/internal/changedetection/state/DefaultTaskArtifactStateCacheAccess.java
new file mode 100644
index 0000000..d120e81
--- /dev/null
+++ b/subprojects/core/src/main/groovy/org/gradle/api/internal/changedetection/state/DefaultTaskArtifactStateCacheAccess.java
@@ -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.state;
+
+import org.gradle.internal.Factory;
+import org.gradle.api.invocation.Gradle;
+import org.gradle.cache.CacheRepository;
+import org.gradle.cache.PersistentCache;
+import org.gradle.cache.PersistentIndexedCache;
+import org.gradle.messaging.serialize.Serializer;
+import org.gradle.cache.internal.FileLockManager;
+import org.gradle.listener.LazyCreationProxy;
+
+import java.io.File;
+
+public class DefaultTaskArtifactStateCacheAccess implements TaskArtifactStateCacheAccess {
+    private final Gradle gradle;
+    private final CacheRepository cacheRepository;
+    private PersistentCache cache;
+    private final Object lock = new Object();
+
+    public DefaultTaskArtifactStateCacheAccess(Gradle gradle, CacheRepository cacheRepository) {
+        this.gradle = gradle;
+        this.cacheRepository = cacheRepository;
+    }
+
+    private PersistentCache getCache() {
+        //TODO SF just do it in the constructor
+        synchronized (lock) {
+            if (cache == null) {
+                cache = cacheRepository
+                        .cache("taskArtifacts")
+                        .forObject(gradle)
+                        .withDisplayName("task artifact state cache")
+                        .withLockMode(FileLockManager.LockMode.Exclusive)
+                        .open();
+            }
+            return cache;
+        }
+    }
+
+    public <K, V> PersistentIndexedCache<K, V> createCache(final String cacheName, final Class<K> keyType, final Class<V> valueType, final Serializer<V> valueSerializer) {
+        Factory<PersistentIndexedCache> factory = new Factory<PersistentIndexedCache>() {
+            public PersistentIndexedCache create() {
+                return getCache().createCache(cacheFile(cacheName), keyType, valueSerializer);
+            }
+        };
+        return new LazyCreationProxy<PersistentIndexedCache>(PersistentIndexedCache.class, factory).getSource();
+
+    }
+
+    private File cacheFile(String cacheName) {
+        return new File(getCache().getBaseDir(), cacheName + ".bin");
+    }
+
+    public <T> T useCache(String operationDisplayName, Factory<? extends T> action) {
+        return getCache().useCache(operationDisplayName, action);
+    }
+
+    public void useCache(String operationDisplayName, Runnable action) {
+        getCache().useCache(operationDisplayName, action);
+    }
+
+    public void longRunningOperation(String operationDisplayName, Runnable action) {
+        getCache().longRunningOperation(operationDisplayName, action);
+    }
+}
\ No newline at end of file
diff --git a/subprojects/core/src/main/groovy/org/gradle/api/internal/changedetection/state/FileCollectionSnapshot.java b/subprojects/core/src/main/groovy/org/gradle/api/internal/changedetection/state/FileCollectionSnapshot.java
new file mode 100755
index 0000000..e5bbf38
--- /dev/null
+++ b/subprojects/core/src/main/groovy/org/gradle/api/internal/changedetection/state/FileCollectionSnapshot.java
@@ -0,0 +1,61 @@
+/*
+ * Copyright 2010 the original author or authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.gradle.api.internal.changedetection.state;
+
+import org.gradle.api.file.FileCollection;
+import org.gradle.util.ChangeListener;
+
+/**
+ * An immutable snapshot of the contents of a collection of files.
+ */
+public interface FileCollectionSnapshot {
+
+    ChangeIterator<String> iterateChangesSince(FileCollectionSnapshot oldSnapshot);
+
+    Diff changesSince(FileCollectionSnapshot oldSnapshot);
+
+    FileCollection getFiles();
+
+    public interface Diff {
+        /**
+         * Applies this diff to the given snapshot. Adds any added or changed files in this diff to the given snapshot.
+         * Removes any removed files in this diff from the given snapshot.
+         *
+         * @param snapshot the snapshot to apply the changes to.
+         * @param listener the listener to notify of changes. The listener can veto a particular change.
+         * @return an updated copy of the provided snapshot
+         */
+        FileCollectionSnapshot applyTo(FileCollectionSnapshot snapshot, ChangeListener<Merge> listener);
+
+        /**
+         * Applies this diff to the given snapshot. Adds any added or changed files in this diff to the given snapshot.
+         * Removes any removed files in this diff from the given snapshot.
+         *
+         * @param snapshot the snapshot to apply the changes to.
+         * @return an updated copy of the provided snapshot
+         */
+        FileCollectionSnapshot applyTo(FileCollectionSnapshot snapshot);
+    }
+
+    public interface Merge {
+        void ignore();
+    }
+
+    interface ChangeIterator<T> {
+        boolean next(ChangeListener<T> listener);
+    }
+}
diff --git a/subprojects/core/src/main/groovy/org/gradle/api/internal/changedetection/state/FileSnapshotRepository.java b/subprojects/core/src/main/groovy/org/gradle/api/internal/changedetection/state/FileSnapshotRepository.java
new file mode 100644
index 0000000..bd0fca2
--- /dev/null
+++ b/subprojects/core/src/main/groovy/org/gradle/api/internal/changedetection/state/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.state;
+
+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/state/FileSnapshotSerializer.java b/subprojects/core/src/main/groovy/org/gradle/api/internal/changedetection/state/FileSnapshotSerializer.java
new file mode 100644
index 0000000..33b3e1e
--- /dev/null
+++ b/subprojects/core/src/main/groovy/org/gradle/api/internal/changedetection/state/FileSnapshotSerializer.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.state;
+
+import org.gradle.messaging.serialize.Decoder;
+import org.gradle.messaging.serialize.Encoder;
+import org.gradle.messaging.serialize.Serializer;
+
+class FileSnapshotSerializer implements Serializer<FileCollectionSnapshot> {
+
+    private final DefaultFileSnapshotterSerializer defaultSnapshotSerializer = new DefaultFileSnapshotterSerializer();
+    private final OutputFilesSnapshotSerializer outputSnapshotSerializer = new OutputFilesSnapshotSerializer();
+
+    public FileCollectionSnapshot read(Decoder decoder) throws Exception {
+        byte kind = decoder.readByte();
+        if (kind == 1) {
+            return defaultSnapshotSerializer.read(decoder);
+        } else if (kind == 2) {
+            return outputSnapshotSerializer.read(decoder);
+        } else {
+            throw new RuntimeException("Unable to read from file snapshot cache. Unexpected value found in the data stream.");
+        }
+    }
+
+    public void write(Encoder encoder, FileCollectionSnapshot value) throws Exception {
+        if (value instanceof DefaultFileSnapshotter.FileCollectionSnapshotImpl) {
+            encoder.writeByte((byte) 1);
+            DefaultFileSnapshotter.FileCollectionSnapshotImpl cached = (DefaultFileSnapshotter.FileCollectionSnapshotImpl) value;
+            defaultSnapshotSerializer.write(encoder, cached);
+        } else if (value instanceof OutputFilesSnapshotter.OutputFilesSnapshot) {
+            encoder.writeByte((byte) 2);
+            OutputFilesSnapshotter.OutputFilesSnapshot cached = (OutputFilesSnapshotter.OutputFilesSnapshot) value;
+            outputSnapshotSerializer.write(encoder, cached);
+        } else {
+            throw new RuntimeException("Unable to write to file snapshot cache. Unexpected type to write: " + value);
+        }
+    }
+}
diff --git a/subprojects/core/src/main/groovy/org/gradle/api/internal/changedetection/state/FileSnapshotter.java b/subprojects/core/src/main/groovy/org/gradle/api/internal/changedetection/state/FileSnapshotter.java
new file mode 100755
index 0000000..92f26db
--- /dev/null
+++ b/subprojects/core/src/main/groovy/org/gradle/api/internal/changedetection/state/FileSnapshotter.java
@@ -0,0 +1,35 @@
+/*
+ * Copyright 2010 the original author or authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.gradle.api.internal.changedetection.state;
+
+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/state/Hasher.java b/subprojects/core/src/main/groovy/org/gradle/api/internal/changedetection/state/Hasher.java
new file mode 100644
index 0000000..8d1975f
--- /dev/null
+++ b/subprojects/core/src/main/groovy/org/gradle/api/internal/changedetection/state/Hasher.java
@@ -0,0 +1,22 @@
+/*
+ * 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.changedetection.state;
+
+import java.io.File;
+
+public interface Hasher {
+    byte[] hash(File file);
+}
diff --git a/subprojects/core/src/main/groovy/org/gradle/api/internal/changedetection/state/MapMergeChangeListener.java b/subprojects/core/src/main/groovy/org/gradle/api/internal/changedetection/state/MapMergeChangeListener.java
new file mode 100644
index 0000000..bdd0917
--- /dev/null
+++ b/subprojects/core/src/main/groovy/org/gradle/api/internal/changedetection/state/MapMergeChangeListener.java
@@ -0,0 +1,67 @@
+/*
+ * Copyright 2010 the original author or authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.gradle.api.internal.changedetection.state;
+
+import org.gradle.util.ChangeListener;
+
+import java.util.Map;
+
+class MapMergeChangeListener<K, V> implements ChangeListener<Map.Entry<K, V>> {
+    private final ChangeListener<FileCollectionSnapshot.Merge> listener;
+    private final Map<K, V> newSnapshots;
+
+    public MapMergeChangeListener(ChangeListener<FileCollectionSnapshot.Merge> listener, Map<K, V> targetMap) {
+        this.listener = listener;
+        this.newSnapshots = targetMap;
+    }
+
+    public void added(Map.Entry<K, V> element) {
+        DefaultMerge merge = new DefaultMerge();
+        listener.added(merge);
+        if (!merge.isIgnore()) {
+            newSnapshots.put(element.getKey(), element.getValue());
+        }
+    }
+
+    public void removed(Map.Entry<K, V> element) {
+        DefaultMerge merge = new DefaultMerge();
+        listener.removed(merge);
+        if (!merge.isIgnore()) {
+            newSnapshots.remove(element.getKey());
+        }
+    }
+
+    public void changed(Map.Entry<K, V> element) {
+        DefaultMerge merge = new DefaultMerge();
+        listener.changed(merge);
+        if (!merge.isIgnore()) {
+            newSnapshots.put(element.getKey(), element.getValue());
+        }
+    }
+
+    private static class DefaultMerge implements FileCollectionSnapshot.Merge {
+        private boolean ignore;
+
+        public boolean isIgnore() {
+            return ignore;
+        }
+
+        public void ignore() {
+            ignore = true;
+        }
+    }
+}
diff --git a/subprojects/core/src/main/groovy/org/gradle/api/internal/changedetection/state/OutputFilesSnapshotSerializer.java b/subprojects/core/src/main/groovy/org/gradle/api/internal/changedetection/state/OutputFilesSnapshotSerializer.java
new file mode 100644
index 0000000..988cab9
--- /dev/null
+++ b/subprojects/core/src/main/groovy/org/gradle/api/internal/changedetection/state/OutputFilesSnapshotSerializer.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.state;
+
+import org.gradle.messaging.serialize.Decoder;
+import org.gradle.messaging.serialize.Encoder;
+import org.gradle.messaging.serialize.Serializer;
+
+import java.util.HashMap;
+import java.util.Map;
+
+class OutputFilesSnapshotSerializer implements Serializer<FileCollectionSnapshot> {
+
+    public FileCollectionSnapshot read(Decoder decoder) throws Exception {
+        Map<String, Long> rootFileIds = new HashMap<String, Long>();
+        int rootFileIdsCount = decoder.readInt();
+        for (int i = 0; i < rootFileIdsCount; i++) {
+            String key = decoder.readString();
+            boolean notNull = decoder.readBoolean();
+            Long value = notNull? decoder.readLong() : null;
+            rootFileIds.put(key, value);
+        }
+        FileSnapshotSerializer serializer = new FileSnapshotSerializer();
+        FileCollectionSnapshot snapshot = serializer.read(decoder);
+
+        return new OutputFilesSnapshotter.OutputFilesSnapshot(rootFileIds, snapshot);
+    }
+
+    public void write(Encoder encoder, FileCollectionSnapshot currentValue) throws Exception {
+        OutputFilesSnapshotter.OutputFilesSnapshot value = (OutputFilesSnapshotter.OutputFilesSnapshot) currentValue;
+        int rootFileIds = value.rootFileIds.size();
+        encoder.writeInt(rootFileIds);
+        for (String key : value.rootFileIds.keySet()) {
+            Long id = value.rootFileIds.get(key);
+            encoder.writeString(key);
+            if (id == null) {
+                encoder.writeBoolean(false);
+            } else {
+                encoder.writeBoolean(true);
+                encoder.writeLong(id);
+            }
+        }
+
+        FileSnapshotSerializer serializer = new FileSnapshotSerializer();
+        serializer.write(encoder, value.filesSnapshot);
+    }
+}
diff --git a/subprojects/core/src/main/groovy/org/gradle/api/internal/changedetection/state/OutputFilesSnapshotter.java b/subprojects/core/src/main/groovy/org/gradle/api/internal/changedetection/state/OutputFilesSnapshotter.java
new file mode 100644
index 0000000..2378429
--- /dev/null
+++ b/subprojects/core/src/main/groovy/org/gradle/api/internal/changedetection/state/OutputFilesSnapshotter.java
@@ -0,0 +1,218 @@
+/*
+ * Copyright 2010 the original author or authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY 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.state;
+
+import org.gradle.api.file.FileCollection;
+import org.gradle.cache.PersistentIndexedCache;
+import org.gradle.internal.id.IdGenerator;
+import org.gradle.messaging.serialize.LongSerializer;
+import org.gradle.util.ChangeListener;
+import org.gradle.util.DiffUtil;
+import org.gradle.util.NoOpChangeListener;
+
+import java.io.File;
+import java.util.*;
+
+/**
+ * Takes a snapshot of the output files of a task. 2 parts to the algorithm:
+ *
+ * <ul>
+ * <li>Collect the unique id for each output file and directory. The unique id is generated when we notice that
+ * a file/directory has been created. The id is regenerated when the file/directory is deleted.</li>
+ *
+ * <li>Collect the hash of each output file and each file in each output directory.</li>
+ * </ul>
+ *
+ */
+public class OutputFilesSnapshotter implements FileSnapshotter {
+    private final FileSnapshotter snapshotter;
+    private final IdGenerator<Long> idGenerator;
+    private TaskArtifactStateCacheAccess cacheAccess;
+    private final PersistentIndexedCache<String, Long> dirIdentiferCache;
+
+    public OutputFilesSnapshotter(FileSnapshotter snapshotter, IdGenerator<Long> idGenerator,
+                                  TaskArtifactStateCacheAccess cacheAccess) {
+        this.snapshotter = snapshotter;
+        this.idGenerator = idGenerator;
+        this.cacheAccess = cacheAccess;
+        dirIdentiferCache = cacheAccess.createCache("outputFileStates", String.class, Long.class, new LongSerializer());
+    }
+
+    public FileCollectionSnapshot emptySnapshot() {
+        return new OutputFilesSnapshot(new HashMap<String, Long>(), snapshotter.emptySnapshot());
+    }
+
+    public OutputFilesSnapshot snapshot(final FileCollection files) {
+        final Map<String, Long> snapshotDirIds = new HashMap<String, Long>();
+        final Set<File> theFiles = files.getFiles();
+        cacheAccess.useCache("create dir snapshots", new Runnable() {
+            public void run() {
+                for (File file : theFiles) {
+                    Long dirId;
+                    if (file.exists()) {
+                        dirId = dirIdentiferCache.get(file.getAbsolutePath());
+                        if (dirId == null) {
+                            dirId = idGenerator.generateId();
+                            dirIdentiferCache.put(file.getAbsolutePath(), dirId);
+                        }
+                    } else {
+                        dirIdentiferCache.remove(file.getAbsolutePath());
+                        dirId = null;
+                    }
+                    snapshotDirIds.put(file.getAbsolutePath(), dirId);
+                }
+
+            }
+        });
+        return new OutputFilesSnapshot(snapshotDirIds, snapshotter.snapshot(files));
+    }
+
+    static class OutputFilesSnapshot implements FileCollectionSnapshot {
+        final Map<String, Long> rootFileIds;
+        final FileCollectionSnapshot filesSnapshot;
+
+        public OutputFilesSnapshot(Map<String, Long> rootFileIds, FileCollectionSnapshot filesSnapshot) {
+            this.rootFileIds = rootFileIds;
+            this.filesSnapshot = filesSnapshot;
+        }
+
+        public FileCollection getFiles() {
+            return filesSnapshot.getFiles();
+        }
+
+        public Diff changesSince(final FileCollectionSnapshot oldSnapshot) {
+            OutputFilesSnapshot other = (OutputFilesSnapshot) oldSnapshot;
+            return new OutputFilesDiff(rootFileIds, other.rootFileIds, filesSnapshot.changesSince(other.filesSnapshot));
+        }
+
+        public ChangeIterator<String> iterateChangesSince(FileCollectionSnapshot oldSnapshot) {
+            final OutputFilesSnapshot other = (OutputFilesSnapshot) oldSnapshot;
+            final ChangeIterator<String> rootFileIdIterator = iterateRootFileIdChanges(other);
+            final ChangeIterator<String> fileIterator = filesSnapshot.iterateChangesSince(other.filesSnapshot);
+
+            final AddIgnoreChangeListenerAdapter listenerAdapter = new AddIgnoreChangeListenerAdapter();
+            return new ChangeIterator<String>() {
+                public boolean next(final ChangeListener<String> listener) {
+                    listenerAdapter.withDelegate(listener);
+                    if (rootFileIdIterator.next(listener)) {
+                        return true;
+                    }
+
+                    while (fileIterator.next(listenerAdapter)) {
+                        if (!listenerAdapter.wasIgnored) {
+                            return true;
+                        }
+                    }
+                    return false;
+                }
+            };
+        }
+
+        private ChangeIterator<String> iterateRootFileIdChanges(final OutputFilesSnapshot other) {
+            // Inlining DiffUtil.diff makes the inefficiencies here a bit more explicit
+            Map<String, Long> added = new HashMap<String, Long>(rootFileIds);
+            added.keySet().removeAll(other.rootFileIds.keySet());
+            final Iterator<String> addedIterator = added.keySet().iterator();
+
+            Map<String, Long> removed = new HashMap<String, Long>(other.rootFileIds);
+            removed.keySet().removeAll(rootFileIds.keySet());
+            final Iterator<String> removedIterator = removed.keySet().iterator();
+
+            Set<String> changed = new HashSet<String>();
+            for (Map.Entry<String, Long> current : rootFileIds.entrySet()) {
+                 // Only care about rootIds that used to exist, and have changed or been removed
+                Long otherValue = other.rootFileIds.get(current.getKey());
+                if (otherValue != null && !otherValue.equals(current.getValue())) {
+                    changed.add(current.getKey());
+                }
+            }
+            final Iterator<String> changedIterator = changed.iterator();
+
+            return new ChangeIterator<String>() {
+                public boolean next(ChangeListener<String> listener) {
+                    if (addedIterator.hasNext()) {
+                        listener.added(addedIterator.next());
+                        return true;
+                    }
+                    if (removedIterator.hasNext()) {
+                        listener.removed(removedIterator.next());
+                        return true;
+                    }
+                    if (changedIterator.hasNext()) {
+                        listener.changed(changedIterator.next());
+                        return true;
+                    }
+
+                    return false;
+                }
+            };
+        }
+    }
+
+    /**
+     * A flyweight wrapper that is used to ignore any added files called.
+     */
+    private static class AddIgnoreChangeListenerAdapter implements ChangeListener<String> {
+        private ChangeListener<String> delegate;
+        boolean wasIgnored;
+
+        private void withDelegate(ChangeListener<String> delegate) {
+            this.delegate = delegate;
+        }
+
+        public void added(String element) {
+            wasIgnored = true;
+        }
+
+        public void removed(String element) {
+            delegate.removed(element);
+            wasIgnored = false;
+        }
+
+        public void changed(String element) {
+            delegate.changed(element);
+            wasIgnored = false;
+        }
+    }
+
+    private static class OutputFilesDiff implements FileCollectionSnapshot.Diff {
+        private final Map<String, Long> newFileIds;
+        private final Map<String, Long> oldFileIds;
+        private final FileCollectionSnapshot.Diff filesDiff;
+
+        public OutputFilesDiff(Map<String, Long> newFileIds, Map<String, Long> oldFileIds,
+                               FileCollectionSnapshot.Diff filesDiff) {
+            this.newFileIds = newFileIds;
+            this.oldFileIds = oldFileIds;
+            this.filesDiff = filesDiff;
+        }
+
+        public FileCollectionSnapshot applyTo(FileCollectionSnapshot snapshot,
+                                              ChangeListener<FileCollectionSnapshot.Merge> listener) {
+            OutputFilesSnapshot other = (OutputFilesSnapshot) snapshot;
+            Map<String, Long> dirIds = new HashMap<String, Long>(other.rootFileIds);
+            DiffUtil.diff(newFileIds, oldFileIds, new MapMergeChangeListener<String, Long>(
+                    new NoOpChangeListener<FileCollectionSnapshot.Merge>(), dirIds));
+            return new OutputFilesSnapshot(newFileIds, filesDiff.applyTo(other.filesSnapshot, listener));
+        }
+
+        public FileCollectionSnapshot applyTo(FileCollectionSnapshot snapshot) {
+            return applyTo(snapshot, new NoOpChangeListener<FileCollectionSnapshot.Merge>());
+        }
+    }
+
+}
diff --git a/subprojects/core/src/main/groovy/org/gradle/api/internal/changedetection/state/TaskArtifactStateCacheAccess.java b/subprojects/core/src/main/groovy/org/gradle/api/internal/changedetection/state/TaskArtifactStateCacheAccess.java
new file mode 100644
index 0000000..82f36df
--- /dev/null
+++ b/subprojects/core/src/main/groovy/org/gradle/api/internal/changedetection/state/TaskArtifactStateCacheAccess.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.state;
+
+import org.gradle.internal.Factory;
+import org.gradle.cache.PersistentIndexedCache;
+import org.gradle.messaging.serialize.Serializer;
+
+public interface TaskArtifactStateCacheAccess {
+    /**
+     * Performs some work against the cache. Acquires exclusive locks the appropriate resources, so that the given action is the only
+     * action to execute across all processes (including this one). Releases the locks and all resources at the end of the action.
+     *
+     * <p>This method is re-entrant, so that an action can call back into this method.</p>
+     */
+    <T> T useCache(String operationDisplayName, Factory<? extends T> action);
+
+    /**
+     * Performs some work against the cache. Acquires exclusive locks the appropriate resources, so that the given action is the only
+     * action to execute across all processes (including this one). Releases the locks and all resources at the end of the action.
+     *
+     * <p>This method is re-entrant, so that an action can call back into this method.</p>
+     */
+    void useCache(String operationDisplayName, Runnable action);
+
+    /**
+     * Performs some long running operation. Releases all locks while the operation is running, and reacquires the locks at the end of
+     * the long running operation.
+     *
+     * <p>This method is re-entrant, so that an action can call back into this method.</p>
+     */
+    void longRunningOperation(String operationDisplayName, Runnable runnable);
+
+    <K, V> PersistentIndexedCache<K, V> createCache(String cacheName, Class<K> keyType, Class<V> valueType, Serializer<V> valueSerializer);
+}
diff --git a/subprojects/core/src/main/groovy/org/gradle/api/internal/changedetection/state/TaskExecution.java b/subprojects/core/src/main/groovy/org/gradle/api/internal/changedetection/state/TaskExecution.java
new file mode 100644
index 0000000..7d70ecb
--- /dev/null
+++ b/subprojects/core/src/main/groovy/org/gradle/api/internal/changedetection/state/TaskExecution.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.changedetection.state;
+
+import java.util.Map;
+import java.util.Set;
+
+/**
+ * The persistent state for a single task execution.
+ */
+public abstract class TaskExecution {
+    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/state/TaskHistoryRepository.java b/subprojects/core/src/main/groovy/org/gradle/api/internal/changedetection/state/TaskHistoryRepository.java
new file mode 100644
index 0000000..4d6cfc9
--- /dev/null
+++ b/subprojects/core/src/main/groovy/org/gradle/api/internal/changedetection/state/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.state;
+
+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/classpath/DefaultModuleRegistry.java b/subprojects/core/src/main/groovy/org/gradle/api/internal/classpath/DefaultModuleRegistry.java
index 0ba1e15..9edcde8 100644
--- a/subprojects/core/src/main/groovy/org/gradle/api/internal/classpath/DefaultModuleRegistry.java
+++ b/subprojects/core/src/main/groovy/org/gradle/api/internal/classpath/DefaultModuleRegistry.java
@@ -19,7 +19,7 @@ import org.gradle.api.UncheckedIOException;
 import org.gradle.api.internal.GradleDistributionLocator;
 import org.gradle.internal.classpath.ClassPath;
 import org.gradle.internal.classpath.DefaultClassPath;
-import org.gradle.util.ClasspathUtil;
+import org.gradle.internal.classloader.ClasspathUtil;
 import org.gradle.util.GUtil;
 
 import java.io.File;
diff --git a/subprojects/core/src/main/groovy/org/gradle/api/internal/classpath/EffectiveClassPath.java b/subprojects/core/src/main/groovy/org/gradle/api/internal/classpath/EffectiveClassPath.java
index e95b9ed..fe14cc8 100644
--- a/subprojects/core/src/main/groovy/org/gradle/api/internal/classpath/EffectiveClassPath.java
+++ b/subprojects/core/src/main/groovy/org/gradle/api/internal/classpath/EffectiveClassPath.java
@@ -18,7 +18,7 @@ package org.gradle.api.internal.classpath;
 
 import org.gradle.api.UncheckedIOException;
 import org.gradle.internal.classpath.DefaultClassPath;
-import org.gradle.util.ClasspathUtil;
+import org.gradle.internal.classloader.ClasspathUtil;
 
 import java.io.File;
 import java.net.URI;
diff --git a/subprojects/core/src/main/groovy/org/gradle/api/internal/classpath/ManifestUtil.java b/subprojects/core/src/main/groovy/org/gradle/api/internal/classpath/ManifestUtil.java
index 3c121c8..193f910 100644
--- a/subprojects/core/src/main/groovy/org/gradle/api/internal/classpath/ManifestUtil.java
+++ b/subprojects/core/src/main/groovy/org/gradle/api/internal/classpath/ManifestUtil.java
@@ -43,7 +43,6 @@ public class ManifestUtil {
         return CollectionUtils.join(" ", paths);
     }
 
-    // TODO:DAZ The returned URI will only be relative if the file is contained in the jarfile directory. Otherwise, an absolute URI is returned.
     private static String constructRelativeClasspathUri(File jarFile, File file) {
         URI jarFileUri = jarFile.getParentFile().toURI();
         URI fileUri = file.toURI();
diff --git a/subprojects/core/src/main/groovy/org/gradle/api/internal/coerce/MethodArgumentsTransformer.java b/subprojects/core/src/main/groovy/org/gradle/api/internal/coerce/MethodArgumentsTransformer.java
new file mode 100644
index 0000000..54ad179
--- /dev/null
+++ b/subprojects/core/src/main/groovy/org/gradle/api/internal/coerce/MethodArgumentsTransformer.java
@@ -0,0 +1,36 @@
+/*
+ * Copyright 2013 the original author or authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.gradle.api.internal.coerce;
+
+/**
+ * Potentially transforms arguments to call a method with.
+ */
+public interface MethodArgumentsTransformer {
+
+    /**
+     * Transforms an argument list to call a method with.
+     *
+     * May return {@code args} if no transform is necessary.
+     *
+     * @param target The object to call the method on
+     * @param methodName The name of the method to call
+     * @param args The args to call the method with
+     * @return The args transformed, or args. Never null.
+     */
+    Object[] transform(Object target, String methodName, Object... args);
+
+}
diff --git a/subprojects/core/src/main/groovy/org/gradle/api/internal/coerce/TypeCoercingMethodArgumentsTransformer.java b/subprojects/core/src/main/groovy/org/gradle/api/internal/coerce/TypeCoercingMethodArgumentsTransformer.java
new file mode 100644
index 0000000..1d82229
--- /dev/null
+++ b/subprojects/core/src/main/groovy/org/gradle/api/internal/coerce/TypeCoercingMethodArgumentsTransformer.java
@@ -0,0 +1,109 @@
+/*
+ * Copyright 2013 the original author or authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.gradle.api.internal.coerce;
+
+import org.gradle.api.Transformer;
+import org.gradle.api.specs.Spec;
+import org.gradle.internal.reflect.JavaReflectionUtil;
+import org.gradle.util.CollectionUtils;
+
+import java.lang.reflect.Method;
+import java.util.ArrayList;
+import java.util.Arrays;
+import java.util.List;
+
+/*
+    This guy is hardcoded to just deal with coercing calls to one arg enum methods, from char sequence values.
+    It will need to be made pluggable as more coercions come online.
+
+    Also need to consider some caching in here as there's potentially a lot of repetitive reflection.
+ */
+public class TypeCoercingMethodArgumentsTransformer implements MethodArgumentsTransformer {
+
+    public Object[] transform(Object target, String methodName, Object... args) {
+        return maybeTransformForEnum(target, methodName, args);
+    }
+
+    private Object[] maybeTransformForEnum(Object target, final String methodName, Object... args) {
+        if (args.length != 1 || !(args[0] instanceof CharSequence)) {
+            return args;
+        }
+
+        final CharSequence charSequenceArg = (CharSequence) args[0];
+
+        final List<Method> enumMethodHolder = new ArrayList<Method>(2);
+        final List<Method> stringMethodHolder = new ArrayList<Method>(1);
+
+        JavaReflectionUtil.searchMethods(target.getClass(), new Transformer<Boolean, Method>() {
+            public Boolean transform(Method method) {
+                Class<?>[] parameterTypes = method.getParameterTypes();
+                if (method.getName().equals(methodName) && parameterTypes.length == 1) {
+                    Class<?> parameterType = parameterTypes[0];
+
+                    if (parameterType.isAssignableFrom(charSequenceArg.getClass())) {
+                        stringMethodHolder.add(method);
+                        return true; // stop searching
+                    } else if (parameterType.isEnum()) {
+                        enumMethodHolder.add(method);
+                        if (enumMethodHolder.size() > 1) {
+                            return true; // stop searching
+                        }
+                    }
+                }
+
+                return false;
+            }
+        });
+
+        // There's a method that takes the uncoerced type
+        if (!stringMethodHolder.isEmpty()) {
+            return args;
+        }
+
+        // There's either no enum method, or more than one
+        if (enumMethodHolder.size() != 1) {
+            return args;
+        }
+
+        // Match, we can try and coerce
+        Method match = enumMethodHolder.get(0);
+        @SuppressWarnings("unchecked")
+        Class<? extends Enum> enumType = (Class<? extends Enum>) match.getParameterTypes()[0];
+        return new Object[]{toEnumValue(enumType, charSequenceArg)};
+    }
+
+    private <T extends Enum<T>> T toEnumValue(Class<T> enumType, CharSequence charSequence) {
+        final String enumString = charSequence.toString();
+        List<T> enumConstants = Arrays.asList(enumType.getEnumConstants());
+        T match = CollectionUtils.findFirst(enumConstants, new Spec<T>() {
+            public boolean isSatisfiedBy(T enumValue) {
+                return enumValue.name().equalsIgnoreCase(enumString);
+            }
+        });
+
+        if (match == null) {
+            throw new TypeCoercionException(
+                    String.format("Cannot coerce string value '%s' to an enum value of type '%s' (valid case insensitive values: %s)",
+                            enumString, enumType.getName(), CollectionUtils.toStringList(Arrays.asList(enumType.getEnumConstants()))
+                    )
+            );
+        } else {
+            return match;
+        }
+    }
+
+}
diff --git a/subprojects/core/src/main/groovy/org/gradle/api/internal/coerce/TypeCoercionException.java b/subprojects/core/src/main/groovy/org/gradle/api/internal/coerce/TypeCoercionException.java
new file mode 100644
index 0000000..25a6be9
--- /dev/null
+++ b/subprojects/core/src/main/groovy/org/gradle/api/internal/coerce/TypeCoercionException.java
@@ -0,0 +1,28 @@
+/*
+ * Copyright 2013 the original author or authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.gradle.api.internal.coerce;
+
+import org.gradle.api.GradleException;
+
+// TODO - this is usually a user error, might need a better name
+public class TypeCoercionException extends GradleException {
+
+    public TypeCoercionException(String message) {
+        super(message);
+    }
+
+}
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 c43f0bd..6e102db 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
@@ -52,6 +52,10 @@ public abstract class AbstractFileResolver implements FileResolver {
         return new BaseDirFileResolver(fileSystem, resolve(path));
     }
 
+    public FileResolver withNoBaseDir() {
+        return new IdentityFileResolver(fileSystem);
+    }
+
     public File resolve(Object path) {
         return resolve(path, PathValidation.NONE);
     }
@@ -244,6 +248,10 @@ public abstract class AbstractFileResolver implements FileResolver {
         return resolveFiles(paths).getAsFileTree();
     }
 
+    public FileTree compositeFileTree(List<FileTree> fileTrees) {
+        return new DefaultCompositeFileTree(fileTrees);
+    }
+
     public ReadableResource resolveResource(Object path) {
         if (path instanceof ReadableResource) {
             return (ReadableResource) path;
diff --git a/subprojects/core/src/main/groovy/org/gradle/api/internal/file/AbstractFileResource.java b/subprojects/core/src/main/groovy/org/gradle/api/internal/file/AbstractFileResource.java
index 2bc0f45..731611a 100644
--- a/subprojects/core/src/main/groovy/org/gradle/api/internal/file/AbstractFileResource.java
+++ b/subprojects/core/src/main/groovy/org/gradle/api/internal/file/AbstractFileResource.java
@@ -21,9 +21,6 @@ import org.gradle.api.resources.ReadableResource;
 import java.io.File;
 import java.net.URI;
 
-/**
- * by Szczepan Faber, created at: 11/23/11
- */
 public abstract class AbstractFileResource implements ReadableResource {
 
     protected final File file;
diff --git a/subprojects/core/src/main/groovy/org/gradle/api/internal/file/AntFileCollectionBuilder.groovy b/subprojects/core/src/main/groovy/org/gradle/api/internal/file/AntFileCollectionBuilder.groovy
index f7d47cf..931fee7 100644
--- a/subprojects/core/src/main/groovy/org/gradle/api/internal/file/AntFileCollectionBuilder.groovy
+++ b/subprojects/core/src/main/groovy/org/gradle/api/internal/file/AntFileCollectionBuilder.groovy
@@ -16,12 +16,9 @@
  
 package org.gradle.api.internal.file
 
-import org.gradle.api.tasks.AntBuilderAware
 import org.gradle.api.file.FileCollection
+import org.gradle.api.tasks.AntBuilderAware
 
-/**
- * @author Hans Dockter
- */
 class AntFileCollectionBuilder implements AntBuilderAware {
     private final FileCollection files
 
diff --git a/subprojects/core/src/main/groovy/org/gradle/api/internal/file/BaseDirFileResolver.java b/subprojects/core/src/main/groovy/org/gradle/api/internal/file/BaseDirFileResolver.java
index 49c4394..e67d58c 100644
--- a/subprojects/core/src/main/groovy/org/gradle/api/internal/file/BaseDirFileResolver.java
+++ b/subprojects/core/src/main/groovy/org/gradle/api/internal/file/BaseDirFileResolver.java
@@ -26,9 +26,6 @@ import java.util.ArrayList;
 import java.util.Arrays;
 import java.util.List;
 
-/**
- * @author Hans Dockter
- */
 public class BaseDirFileResolver extends AbstractFileResolver {
     private final File baseDir;
 
diff --git a/subprojects/core/src/main/groovy/org/gradle/api/internal/file/CopyActionProcessingStreamAction.java b/subprojects/core/src/main/groovy/org/gradle/api/internal/file/CopyActionProcessingStreamAction.java
new file mode 100644
index 0000000..a8fb36c
--- /dev/null
+++ b/subprojects/core/src/main/groovy/org/gradle/api/internal/file/CopyActionProcessingStreamAction.java
@@ -0,0 +1,25 @@
+/*
+ * Copyright 2013 the original author or authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY 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.copy.FileCopyDetailsInternal;
+
+public interface CopyActionProcessingStreamAction {
+
+    void processFile(FileCopyDetailsInternal details);
+
+}
diff --git a/subprojects/core/src/main/groovy/org/gradle/api/internal/file/DefaultCompositeFileTree.java b/subprojects/core/src/main/groovy/org/gradle/api/internal/file/DefaultCompositeFileTree.java
new file mode 100644
index 0000000..19aa306
--- /dev/null
+++ b/subprojects/core/src/main/groovy/org/gradle/api/internal/file/DefaultCompositeFileTree.java
@@ -0,0 +1,45 @@
+/*
+ * Copyright 2013 the original author or authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY 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.internal.file.collections.FileCollectionResolveContext;
+
+import java.util.List;
+
+public class DefaultCompositeFileTree extends CompositeFileTree {
+    private final List<FileTree> fileTrees;
+
+    public DefaultCompositeFileTree(List<FileTree> fileTrees) {
+        this.fileTrees = fileTrees;
+    }
+
+    @Override
+    public void resolve(FileCollectionResolveContext context) {
+        context.add(fileTrees);
+    }
+
+    @Override
+    protected List<FileTree> getSourceCollections() {
+        return fileTrees;
+    }
+
+    @Override
+    public String getDisplayName() {
+        return "file tree";
+    }
+}
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 7a19cbf..a370a9e 100755
--- 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
@@ -16,20 +16,26 @@
 package org.gradle.api.internal.file;
 
 import groovy.lang.Closure;
+import org.gradle.api.Action;
 import org.gradle.api.InvalidUserDataException;
 import org.gradle.api.PathValidation;
 import org.gradle.api.file.*;
+import org.gradle.api.internal.ClosureBackedAction;
 import org.gradle.api.internal.ProcessOperations;
 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.file.copy.CopySpecInternal;
+import org.gradle.api.internal.file.copy.DefaultCopySpec;
+import org.gradle.api.internal.file.copy.DeleteActionImpl;
+import org.gradle.api.internal.file.copy.FileCopier;
 import org.gradle.api.internal.resources.DefaultResourceHandler;
 import org.gradle.api.internal.tasks.TaskResolver;
 import org.gradle.api.resources.ReadableResource;
 import org.gradle.api.tasks.WorkResult;
+import org.gradle.internal.reflect.Instantiator;
 import org.gradle.process.ExecResult;
 import org.gradle.process.internal.DefaultExecAction;
 import org.gradle.process.internal.DefaultJavaExecAction;
@@ -49,13 +55,15 @@ public class DefaultFileOperations implements FileOperations, ProcessOperations
     private final FileResolver fileResolver;
     private final TaskResolver taskResolver;
     private final TemporaryFileProvider temporaryFileProvider;
+    private final Instantiator instantiator;
     private DeleteAction deleteAction;
     private final DefaultResourceHandler resourceHandler;
 
-    public DefaultFileOperations(FileResolver fileResolver, TaskResolver taskResolver, TemporaryFileProvider temporaryFileProvider) {
+    public DefaultFileOperations(FileResolver fileResolver, TaskResolver taskResolver, TemporaryFileProvider temporaryFileProvider, Instantiator instantiator) {
         this.fileResolver = fileResolver;
         this.taskResolver = taskResolver;
         this.temporaryFileProvider = temporaryFileProvider;
+        this.instantiator = instantiator;
         this.deleteAction = new DeleteActionImpl(fileResolver);
         this.resourceHandler = new DefaultResourceHandler(fileResolver);
     }
@@ -81,7 +89,7 @@ public class DefaultFileOperations implements FileOperations, ProcessOperations
     }
 
     public ConfigurableFileTree fileTree(Object baseDir) {
-        return new DefaultConfigurableFileTree(baseDir, fileResolver, taskResolver);
+        return new DefaultConfigurableFileTree(baseDir, fileResolver, taskResolver, instantiator);
     }
 
     public ConfigurableFileTree fileTree(Object baseDir, Closure closure) {
@@ -89,12 +97,12 @@ public class DefaultFileOperations implements FileOperations, ProcessOperations
     }
 
     public ConfigurableFileTree fileTree(Map<String, ?> args) {
-        return new DefaultConfigurableFileTree(args, fileResolver, taskResolver);
+        return new DefaultConfigurableFileTree(args, fileResolver, taskResolver, instantiator);
     }
 
     public ConfigurableFileTree fileTree(Closure closure) {
         // This method is deprecated, but the deprecation warning is added on public classes that delegate to this. 
-        return configure(closure, new DefaultConfigurableFileTree(Collections.emptyMap(), fileResolver, taskResolver));
+        return configure(closure, new DefaultConfigurableFileTree(Collections.emptyMap(), fileResolver, taskResolver, instantiator));
     }
 
     public FileTree zipTree(Object zipPath) {
@@ -130,13 +138,23 @@ public class DefaultFileOperations implements FileOperations, ProcessOperations
     }
 
     public WorkResult copy(Closure closure) {
-        CopyActionImpl action = configure(closure, new FileCopyActionImpl(fileResolver, new FileCopySpecVisitor()));
-        action.execute();
-        return action;
+        FileCopier copyAction = new FileCopier(instantiator, fileResolver);
+        return copyAction.copy(new ClosureBackedAction<CopySpec>(closure));
     }
 
-    public CopySpec copySpec(Closure closure) {
-        return configure(closure, new CopySpecImpl(fileResolver));
+    public WorkResult sync(Action<? super CopySpec> action) {
+        FileCopier copyAction = new FileCopier(instantiator, fileResolver);
+        return copyAction.sync(action);
+    }
+
+    public CopySpecInternal copySpec(Closure closure) {
+        return copySpec(new ClosureBackedAction<CopySpec>(closure));
+    }
+
+    public CopySpecInternal copySpec(Action<? super CopySpec> action) {
+        DefaultCopySpec copySpec = instantiator.newInstance(DefaultCopySpec.class, fileResolver, instantiator);
+        action.execute(copySpec);
+        return copySpec;
     }
 
     public FileResolver getFileResolver() {
@@ -152,12 +170,12 @@ public class DefaultFileOperations implements FileOperations, ProcessOperations
     }
 
     public ExecResult javaexec(Closure cl) {
-        JavaExecAction javaExecAction = ConfigureUtil.configure(cl, new DefaultJavaExecAction(fileResolver));
+        JavaExecAction javaExecAction = ConfigureUtil.configure(cl, instantiator.newInstance(DefaultJavaExecAction.class, fileResolver));
         return javaExecAction.execute();
     }
 
     public ExecResult exec(Closure cl) {
-        ExecAction execAction = ConfigureUtil.configure(cl, new DefaultExecAction(fileResolver));
+        ExecAction execAction = ConfigureUtil.configure(cl, instantiator.newInstance(DefaultExecAction.class, fileResolver));
         return execAction.execute();
     }
 
diff --git a/subprojects/core/src/main/groovy/org/gradle/api/internal/file/DefaultTemporaryFileProvider.java b/subprojects/core/src/main/groovy/org/gradle/api/internal/file/DefaultTemporaryFileProvider.java
index 5e0d564..96311f5 100755
--- a/subprojects/core/src/main/groovy/org/gradle/api/internal/file/DefaultTemporaryFileProvider.java
+++ b/subprojects/core/src/main/groovy/org/gradle/api/internal/file/DefaultTemporaryFileProvider.java
@@ -24,8 +24,9 @@ import org.gradle.util.GFileUtils;
 
 import java.io.File;
 import java.io.IOException;
+import java.io.Serializable;
 
-public class DefaultTemporaryFileProvider implements TemporaryFileProvider {
+public class DefaultTemporaryFileProvider implements TemporaryFileProvider, Serializable {
     private final Factory<File> baseDirFactory;
 
     public DefaultTemporaryFileProvider(final Factory<File> fileFactory) {
diff --git a/subprojects/core/src/main/groovy/org/gradle/api/internal/file/FileOperations.java b/subprojects/core/src/main/groovy/org/gradle/api/internal/file/FileOperations.java
index 30ec18b..0197e27 100755
--- a/subprojects/core/src/main/groovy/org/gradle/api/internal/file/FileOperations.java
+++ b/subprojects/core/src/main/groovy/org/gradle/api/internal/file/FileOperations.java
@@ -16,11 +16,13 @@
 package org.gradle.api.internal.file;
 
 import groovy.lang.Closure;
+import org.gradle.api.Action;
 import org.gradle.api.PathValidation;
 import org.gradle.api.file.ConfigurableFileCollection;
 import org.gradle.api.file.ConfigurableFileTree;
 import org.gradle.api.file.CopySpec;
 import org.gradle.api.file.FileTree;
+import org.gradle.api.internal.file.copy.CopySpecInternal;
 import org.gradle.api.resources.ResourceHandler;
 import org.gradle.api.tasks.WorkResult;
 
@@ -58,8 +60,12 @@ public interface FileOperations {
 
     CopySpec copySpec(Closure closure);
 
+    CopySpecInternal copySpec(Action<? super CopySpec> action);
+
     WorkResult copy(Closure closure);
 
+    WorkResult sync(Action<? super CopySpec> action);
+
     File mkdir(Object path);
 
     boolean delete(Object... paths);
diff --git a/subprojects/core/src/main/groovy/org/gradle/api/internal/file/FileResolver.java b/subprojects/core/src/main/groovy/org/gradle/api/internal/file/FileResolver.java
index 0d5c954..0f4eae7 100644
--- a/subprojects/core/src/main/groovy/org/gradle/api/internal/file/FileResolver.java
+++ b/subprojects/core/src/main/groovy/org/gradle/api/internal/file/FileResolver.java
@@ -24,6 +24,7 @@ import org.gradle.internal.Factory;
 
 import java.io.File;
 import java.net.URI;
+import java.util.List;
 
 public interface FileResolver {
     File resolve(Object path);
@@ -38,6 +39,8 @@ public interface FileResolver {
 
     FileTree resolveFilesAsTree(Object... paths);
 
+    FileTree compositeFileTree(List<FileTree> fileTrees);
+
     URI resolveUri(Object path);
 
     String resolveAsRelativePath(Object path);
@@ -48,5 +51,10 @@ public interface FileResolver {
      */
     FileResolver withBaseDir(Object path);
 
+    /**
+     * Creates a new resolver with no base directory.
+     */
+    FileResolver withNoBaseDir();
+
     NotationParser<File> asNotationParser();
 }
diff --git a/subprojects/core/src/main/groovy/org/gradle/api/internal/file/FileResource.java b/subprojects/core/src/main/groovy/org/gradle/api/internal/file/FileResource.java
index 55c0a62..35472cf 100644
--- a/subprojects/core/src/main/groovy/org/gradle/api/internal/file/FileResource.java
+++ b/subprojects/core/src/main/groovy/org/gradle/api/internal/file/FileResource.java
@@ -21,9 +21,6 @@ import org.gradle.api.resources.MissingResourceException;
 
 import java.io.*;
 
-/**
- * by Szczepan Faber, created at: 11/22/11
- */
 public class FileResource extends AbstractFileResource {
 
     public FileResource(File file) {
diff --git a/subprojects/core/src/main/groovy/org/gradle/api/internal/file/IdentityFileResolver.java b/subprojects/core/src/main/groovy/org/gradle/api/internal/file/IdentityFileResolver.java
index 3f1068d..8cfc63c 100644
--- a/subprojects/core/src/main/groovy/org/gradle/api/internal/file/IdentityFileResolver.java
+++ b/subprojects/core/src/main/groovy/org/gradle/api/internal/file/IdentityFileResolver.java
@@ -15,6 +15,7 @@
  */
 package org.gradle.api.internal.file;
 
+import org.gradle.internal.nativeplatform.filesystem.FileSystem;
 import org.gradle.internal.nativeplatform.filesystem.FileSystems;
 
 import java.io.File;
@@ -22,14 +23,16 @@ import java.io.File;
 /**
  * FileResolver that uses the file provided to it or constructs one from the toString of the provided object. Used in cases where a FileResolver is needed by the infrastructure, but no base directory
  * can be known.
- *
- * @author Steve Appling
  */
 public class IdentityFileResolver extends AbstractFileResolver {
     public IdentityFileResolver() {
         super(FileSystems.getDefault());
     }
 
+    public IdentityFileResolver(FileSystem fileSystem) {
+        super(fileSystem);
+    }
+
     @Override
     protected File doResolve(Object path) {
         File file = convertObjectToFile(path);
diff --git a/subprojects/core/src/main/groovy/org/gradle/api/internal/file/MaybeCompressedFileResource.java b/subprojects/core/src/main/groovy/org/gradle/api/internal/file/MaybeCompressedFileResource.java
index cbc1a15..27fee31 100644
--- a/subprojects/core/src/main/groovy/org/gradle/api/internal/file/MaybeCompressedFileResource.java
+++ b/subprojects/core/src/main/groovy/org/gradle/api/internal/file/MaybeCompressedFileResource.java
@@ -26,9 +26,6 @@ import org.gradle.api.tasks.bundling.Compression;
 import java.io.InputStream;
 import java.net.URI;
 
-/**
- * by Szczepan Faber, created at: 11/23/11
- */
 public class MaybeCompressedFileResource implements ReadableResource {
 
     private final ReadableResource resource;
diff --git a/subprojects/core/src/main/groovy/org/gradle/api/internal/file/archive/TarCopyAction.java b/subprojects/core/src/main/groovy/org/gradle/api/internal/file/archive/TarCopyAction.java
new file mode 100644
index 0000000..b00d7c8
--- /dev/null
+++ b/subprojects/core/src/main/groovy/org/gradle/api/internal/file/archive/TarCopyAction.java
@@ -0,0 +1,117 @@
+/*
+ * 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.archive;
+
+import org.apache.commons.io.IOUtils;
+import org.apache.tools.tar.TarEntry;
+import org.apache.tools.tar.TarOutputStream;
+import org.apache.tools.zip.UnixStat;
+import org.gradle.api.GradleException;
+import org.gradle.api.file.FileCopyDetails;
+import org.gradle.api.internal.file.CopyActionProcessingStreamAction;
+import org.gradle.api.internal.file.archive.compression.ArchiveOutputStreamFactory;
+import org.gradle.api.internal.file.copy.CopyAction;
+import org.gradle.api.internal.file.copy.CopyActionProcessingStream;
+import org.gradle.api.internal.file.copy.FileCopyDetailsInternal;
+import org.gradle.api.internal.tasks.SimpleWorkResult;
+import org.gradle.api.tasks.WorkResult;
+import org.gradle.internal.UncheckedException;
+
+import java.io.File;
+import java.io.OutputStream;
+
+public class TarCopyAction implements CopyAction {
+    private final File tarFile;
+    private final ArchiveOutputStreamFactory compressor;
+
+    public TarCopyAction(File tarFile, ArchiveOutputStreamFactory compressor) {
+        this.tarFile = tarFile;
+        this.compressor = compressor;
+    }
+
+    public WorkResult execute(CopyActionProcessingStream stream) {
+        final TarOutputStream tarOutStr;
+
+        OutputStream outStr;
+        try {
+            outStr = compressor.createArchiveOutputStream(tarFile);
+        } catch (Exception e) {
+            throw new GradleException(String.format("Could not create TAR '%s'.", tarFile), e);
+        }
+
+        try {
+            tarOutStr = new TarOutputStream(outStr);
+        } catch (Exception e) {
+            IOUtils.closeQuietly(outStr);
+            throw new GradleException(String.format("Could not create TAR '%s'.", tarFile), e);
+        }
+
+        tarOutStr.setLongFileMode(TarOutputStream.LONGFILE_GNU);
+
+        try {
+            stream.process(new StreamAction(tarOutStr));
+        } catch (Exception e) {
+            UncheckedException.throwAsUncheckedException(e);
+        } finally {
+            IOUtils.closeQuietly(tarOutStr);
+        }
+
+        return new SimpleWorkResult(true);
+    }
+
+    private class StreamAction implements CopyActionProcessingStreamAction {
+        private final TarOutputStream tarOutStr;
+
+        public StreamAction(TarOutputStream tarOutStr) {
+            this.tarOutStr = tarOutStr;
+        }
+
+        public void processFile(FileCopyDetailsInternal details) {
+            if (details.isDirectory()) {
+                visitDir(details);
+            } else {
+                visitFile(details);
+            }
+        }
+
+        private void visitFile(FileCopyDetails fileDetails) {
+            try {
+                TarEntry archiveEntry = new TarEntry(fileDetails.getRelativePath().getPathString());
+                archiveEntry.setModTime(fileDetails.getLastModified());
+                archiveEntry.setSize(fileDetails.getSize());
+                archiveEntry.setMode(UnixStat.FILE_FLAG | fileDetails.getMode());
+                tarOutStr.putNextEntry(archiveEntry);
+                fileDetails.copyTo(tarOutStr);
+                tarOutStr.closeEntry();
+            } catch (Exception e) {
+                throw new GradleException(String.format("Could not add %s to TAR '%s'.", fileDetails, tarFile), e);
+            }
+        }
+
+        private void visitDir(FileCopyDetails dirDetails) {
+            try {
+                // Trailing slash on name indicates entry is a directory
+                TarEntry archiveEntry = new TarEntry(dirDetails.getRelativePath().getPathString() + '/');
+                archiveEntry.setModTime(dirDetails.getLastModified());
+                archiveEntry.setMode(UnixStat.DIR_FLAG | dirDetails.getMode());
+                tarOutStr.putNextEntry(archiveEntry);
+                tarOutStr.closeEntry();
+            } catch (Exception e) {
+                throw new GradleException(String.format("Could not add %s to TAR '%s'.", dirDetails, tarFile), e);
+            }
+        }
+    }
+}
diff --git a/subprojects/core/src/main/groovy/org/gradle/api/internal/file/archive/TarCopySpecVisitor.java b/subprojects/core/src/main/groovy/org/gradle/api/internal/file/archive/TarCopySpecVisitor.java
deleted file mode 100644
index 506dc4c..0000000
--- a/subprojects/core/src/main/groovy/org/gradle/api/internal/file/archive/TarCopySpecVisitor.java
+++ /dev/null
@@ -1,88 +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.archive;
-
-import org.apache.tools.tar.TarEntry;
-import org.apache.tools.tar.TarOutputStream;
-import org.apache.tools.zip.UnixStat;
-import org.gradle.api.GradleException;
-import org.gradle.api.UncheckedIOException;
-import org.gradle.api.file.FileVisitDetails;
-import org.gradle.api.internal.file.copy.ArchiveCopyAction;
-import org.gradle.api.internal.file.copy.CopyAction;
-import org.gradle.api.internal.file.copy.EmptyCopySpecVisitor;
-
-import java.io.File;
-import java.io.IOException;
-import java.io.OutputStream;
-
-public class TarCopySpecVisitor extends EmptyCopySpecVisitor {
-    private TarOutputStream tarOutStr;
-    private File tarFile;
-
-    public void startVisit(CopyAction action) {
-        ArchiveCopyAction archiveAction = (ArchiveCopyAction) action;
-        try {
-            tarFile = archiveAction.getArchivePath();
-            OutputStream outStr = archiveAction.getCompressor().createArchiveOutputStream(tarFile);
-            tarOutStr = new TarOutputStream(outStr);
-            tarOutStr.setLongFileMode(TarOutputStream.LONGFILE_GNU);
-        } catch (Exception e) {
-            throw new GradleException(String.format("Could not create TAR '%s'.", tarFile), e);
-        }
-    }
-
-    public void endVisit() {
-        try {
-            tarOutStr.close();
-        } catch (IOException e) {
-            throw new UncheckedIOException(e);
-        } finally {
-            tarOutStr = null;
-        }
-    }
-
-    public void visitFile(FileVisitDetails fileDetails) {
-        try {
-            TarEntry archiveEntry = new TarEntry(fileDetails.getRelativePath().getPathString());
-            archiveEntry.setModTime(fileDetails.getLastModified());
-            archiveEntry.setSize(fileDetails.getSize());
-            archiveEntry.setMode(UnixStat.FILE_FLAG | fileDetails.getMode());
-            tarOutStr.putNextEntry(archiveEntry);
-            fileDetails.copyTo(tarOutStr);
-            tarOutStr.closeEntry();
-        } catch (Exception e) {
-            throw new GradleException(String.format("Could not add %s to TAR '%s'.", fileDetails, tarFile), e);
-        }
-    }
-
-    public void visitDir(FileVisitDetails dirDetails) {
-        try {
-            // Trailing slash on name indicates entry is a directory
-            TarEntry archiveEntry = new TarEntry(dirDetails.getRelativePath().getPathString() + '/');
-            archiveEntry.setModTime(dirDetails.getLastModified());
-            archiveEntry.setMode(UnixStat.DIR_FLAG | dirDetails.getMode());
-            tarOutStr.putNextEntry(archiveEntry);
-            tarOutStr.closeEntry();
-        } catch (Exception e) {
-            throw new GradleException(String.format("Could not add %s to TAR '%s'.", dirDetails, tarFile), e);
-        }
-    }
-
-    public boolean getDidWork() {
-        return true;
-    }
-}
diff --git a/subprojects/core/src/main/groovy/org/gradle/api/internal/file/archive/ZipCopyAction.java b/subprojects/core/src/main/groovy/org/gradle/api/internal/file/archive/ZipCopyAction.java
index 14ebd22..cad830c 100644
--- a/subprojects/core/src/main/groovy/org/gradle/api/internal/file/archive/ZipCopyAction.java
+++ b/subprojects/core/src/main/groovy/org/gradle/api/internal/file/archive/ZipCopyAction.java
@@ -1,5 +1,5 @@
 /*
- * Copyright 2009 the original author or authors.
+ * Copyright 2010 the original author or authors.
  *
  * Licensed under the Apache License, Version 2.0 (the "License");
  * you may not use this file except in compliance with the License.
@@ -15,10 +15,91 @@
  */
 package org.gradle.api.internal.file.archive;
 
-import org.gradle.api.internal.file.copy.ArchiveCopyAction;
+import org.apache.commons.io.IOUtils;
+import org.apache.tools.zip.UnixStat;
+import org.apache.tools.zip.ZipEntry;
+import org.apache.tools.zip.ZipOutputStream;
+import org.gradle.api.GradleException;
+import org.gradle.api.file.FileCopyDetails;
+import org.gradle.api.internal.file.CopyActionProcessingStreamAction;
+import org.gradle.api.internal.file.copy.CopyAction;
+import org.gradle.api.internal.file.copy.CopyActionProcessingStream;
+import org.gradle.api.internal.file.copy.FileCopyDetailsInternal;
 import org.gradle.api.internal.file.copy.ZipCompressor;
+import org.gradle.api.internal.tasks.SimpleWorkResult;
+import org.gradle.api.tasks.WorkResult;
+import org.gradle.internal.UncheckedException;
 
-public interface ZipCopyAction extends ArchiveCopyAction {
+import java.io.File;
 
-    public ZipCompressor getCompressor();
+public class ZipCopyAction implements CopyAction {
+    private final File zipFile;
+    private final ZipCompressor compressor;
+
+    public ZipCopyAction(File zipFile, ZipCompressor compressor) {
+        this.zipFile = zipFile;
+        this.compressor = compressor;
+    }
+
+    public WorkResult execute(CopyActionProcessingStream stream) {
+        final ZipOutputStream zipOutStr;
+
+        try {
+            zipOutStr = compressor.createArchiveOutputStream(zipFile);
+        } catch (Exception e) {
+            throw new GradleException(String.format("Could not create ZIP '%s'.", zipFile), e);
+        }
+
+        try {
+            stream.process(new StreamAction(zipOutStr));
+        } catch (Exception e) {
+            UncheckedException.throwAsUncheckedException(e);
+        } finally {
+            IOUtils.closeQuietly(zipOutStr);
+        }
+
+        return new SimpleWorkResult(true);
+    }
+
+    private class StreamAction implements CopyActionProcessingStreamAction {
+        private final ZipOutputStream zipOutStr;
+
+        public StreamAction(ZipOutputStream zipOutStr) {
+            this.zipOutStr = zipOutStr;
+        }
+
+        public void processFile(FileCopyDetailsInternal details) {
+            if (details.isDirectory()) {
+                visitDir(details);
+            } else {
+                visitFile(details);
+            }
+        }
+
+        private void visitFile(FileCopyDetails fileDetails) {
+            try {
+                ZipEntry archiveEntry = new ZipEntry(fileDetails.getRelativePath().getPathString());
+                archiveEntry.setTime(fileDetails.getLastModified());
+                archiveEntry.setUnixMode(UnixStat.FILE_FLAG | fileDetails.getMode());
+                zipOutStr.putNextEntry(archiveEntry);
+                fileDetails.copyTo(zipOutStr);
+                zipOutStr.closeEntry();
+            } catch (Exception e) {
+                throw new GradleException(String.format("Could not add %s to ZIP '%s'.", fileDetails, zipFile), e);
+            }
+        }
+
+        private void visitDir(FileCopyDetails dirDetails) {
+            try {
+                // Trailing slash in name indicates that entry is a directory
+                ZipEntry archiveEntry = new ZipEntry(dirDetails.getRelativePath().getPathString() + '/');
+                archiveEntry.setTime(dirDetails.getLastModified());
+                archiveEntry.setUnixMode(UnixStat.DIR_FLAG | dirDetails.getMode());
+                zipOutStr.putNextEntry(archiveEntry);
+                zipOutStr.closeEntry();
+            } catch (Exception e) {
+                throw new GradleException(String.format("Could not add %s to ZIP '%s'.", dirDetails, zipFile), e);
+            }
+        }
+    }
 }
diff --git a/subprojects/core/src/main/groovy/org/gradle/api/internal/file/archive/ZipCopySpecVisitor.java b/subprojects/core/src/main/groovy/org/gradle/api/internal/file/archive/ZipCopySpecVisitor.java
deleted file mode 100644
index 1491f4b..0000000
--- a/subprojects/core/src/main/groovy/org/gradle/api/internal/file/archive/ZipCopySpecVisitor.java
+++ /dev/null
@@ -1,81 +0,0 @@
-/*
- * Copyright 2010 the original author or authors.
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- *      http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-package org.gradle.api.internal.file.archive;
-
-import org.apache.tools.zip.*;
-import org.gradle.api.GradleException;
-import org.gradle.api.UncheckedIOException;
-import org.gradle.api.internal.file.copy.CopyAction;
-import org.gradle.api.file.FileVisitDetails;
-import org.gradle.api.internal.file.copy.EmptyCopySpecVisitor;
-
-import java.io.File;
-import java.io.IOException;
-
-public class ZipCopySpecVisitor extends EmptyCopySpecVisitor {
-    private ZipOutputStream zipOutStr;
-    private File zipFile;
-
-    public void startVisit(CopyAction action) {
-        ZipCopyAction archiveAction = (ZipCopyAction) action;
-        zipFile = archiveAction.getArchivePath();
-        try {
-            zipOutStr = archiveAction.getCompressor().createArchiveOutputStream(zipFile);
-        } catch (Exception e) {
-            throw new GradleException(String.format("Could not create ZIP '%s'.", zipFile), e);
-        }
-    }
-
-    public void endVisit() {
-        try {
-            zipOutStr.close();
-        } catch (IOException e) {
-            throw new UncheckedIOException(e);
-        } finally {
-            zipOutStr = null;
-        }
-    }
-
-    public void visitFile(FileVisitDetails fileDetails) {
-        try {
-            ZipEntry archiveEntry = new ZipEntry(fileDetails.getRelativePath().getPathString());
-            archiveEntry.setTime(fileDetails.getLastModified());
-            archiveEntry.setUnixMode(UnixStat.FILE_FLAG | fileDetails.getMode());
-            zipOutStr.putNextEntry(archiveEntry);
-            fileDetails.copyTo(zipOutStr);
-            zipOutStr.closeEntry();
-        } catch (Exception e) {
-            throw new GradleException(String.format("Could not add %s to ZIP '%s'.", fileDetails, zipFile), e);
-        }
-    }
-
-    public void visitDir(FileVisitDetails dirDetails) {
-        try {
-            // Trailing slash in name indicates that entry is a directory
-            ZipEntry archiveEntry = new ZipEntry(dirDetails.getRelativePath().getPathString() + '/');
-            archiveEntry.setTime(dirDetails.getLastModified());
-            archiveEntry.setUnixMode(UnixStat.DIR_FLAG | dirDetails.getMode());
-            zipOutStr.putNextEntry(archiveEntry);
-            zipOutStr.closeEntry();
-        } catch (Exception e) {
-            throw new GradleException(String.format("Could not add %s to ZIP '%s'.", dirDetails, zipFile), e);
-        }
-    }
-
-    public boolean getDidWork() {
-        return true;
-    }
-}
diff --git a/subprojects/core/src/main/groovy/org/gradle/api/internal/file/archive/compression/Bzip2Archiver.java b/subprojects/core/src/main/groovy/org/gradle/api/internal/file/archive/compression/Bzip2Archiver.java
index b923418..fb4f1d6 100644
--- a/subprojects/core/src/main/groovy/org/gradle/api/internal/file/archive/compression/Bzip2Archiver.java
+++ b/subprojects/core/src/main/groovy/org/gradle/api/internal/file/archive/compression/Bzip2Archiver.java
@@ -28,9 +28,6 @@ import java.io.InputStream;
 import java.io.OutputStream;
 import java.net.URI;
 
-/**
- * by Szczepan Faber, created at: 11/16/11
- */
 public class Bzip2Archiver implements ReadableResource {
 
     private final ReadableResource resource;
diff --git a/subprojects/core/src/main/groovy/org/gradle/api/internal/file/archive/compression/GzipArchiver.java b/subprojects/core/src/main/groovy/org/gradle/api/internal/file/archive/compression/GzipArchiver.java
index 619d812..4cef989 100644
--- a/subprojects/core/src/main/groovy/org/gradle/api/internal/file/archive/compression/GzipArchiver.java
+++ b/subprojects/core/src/main/groovy/org/gradle/api/internal/file/archive/compression/GzipArchiver.java
@@ -28,9 +28,6 @@ import java.net.URI;
 import java.util.zip.GZIPInputStream;
 import java.util.zip.GZIPOutputStream;
 
-/**
- * by Szczepan Faber, created at: 11/16/11
- */
 public class GzipArchiver implements ReadableResource {
 
     private ReadableResource resource;
diff --git a/subprojects/core/src/main/groovy/org/gradle/api/internal/file/archive/compression/SimpleCompressor.java b/subprojects/core/src/main/groovy/org/gradle/api/internal/file/archive/compression/SimpleCompressor.java
index c47a5df..f886266 100644
--- a/subprojects/core/src/main/groovy/org/gradle/api/internal/file/archive/compression/SimpleCompressor.java
+++ b/subprojects/core/src/main/groovy/org/gradle/api/internal/file/archive/compression/SimpleCompressor.java
@@ -20,9 +20,6 @@ import java.io.File;
 import java.io.FileOutputStream;
 import java.io.OutputStream;
 
-/**
- * by Szczepan Faber, created at: 11/16/11
- */
 public class SimpleCompressor implements ArchiveOutputStreamFactory {
 
     public OutputStream createArchiveOutputStream(File destination) {
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
index 21acdcf..ee9b559 100644
--- 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
@@ -16,21 +16,21 @@
 package org.gradle.api.internal.file.collections;
 
 import groovy.lang.Closure;
+import org.gradle.api.Action;
 import org.gradle.api.InvalidUserDataException;
 import org.gradle.api.file.ConfigurableFileTree;
+import org.gradle.api.file.CopySpec;
 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.file.copy.FileCopier;
 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.internal.reflect.Instantiator;
 import org.gradle.util.ConfigureUtil;
 
 import java.io.File;
@@ -38,23 +38,22 @@ 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;
+    private final Instantiator instantiator;
 
-    public DefaultConfigurableFileTree(Object dir, FileResolver resolver, TaskResolver taskResolver) {
-        this(Collections.singletonMap("dir", dir), resolver, taskResolver);
+    public DefaultConfigurableFileTree(Object dir, FileResolver resolver, TaskResolver taskResolver, Instantiator instantiator) {
+        this(Collections.singletonMap("dir", dir), resolver, taskResolver, instantiator);
     }
 
-    public DefaultConfigurableFileTree(Map<String, ?> args, FileResolver resolver, TaskResolver taskResolver) {
-        this.resolver = resolver != null ? resolver : new IdentityFileResolver();
+    public DefaultConfigurableFileTree(Map<String, ?> args, FileResolver resolver, TaskResolver taskResolver, Instantiator instantiator) {
+        this.resolver = resolver;
         ConfigureUtil.configureByMap(args, this);
         buildDependency = new DefaultTaskDependency(taskResolver);
+        this.instantiator = instantiator;
     }
 
     public PatternSet getPatterns() {
@@ -82,12 +81,14 @@ public class DefaultConfigurableFileTree extends CompositeFileTree implements Co
         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 WorkResult copy(final Closure closure) {
+        FileCopier copyAction = new FileCopier(instantiator, resolver);
+        return copyAction.copy(new Action<CopySpec>() {
+            public void execute(CopySpec copySpec) {
+                copySpec.from(DefaultConfigurableFileTree.this);
+                ConfigureUtil.configure(closure, copySpec);
+            }
+        });
     }
 
     public Set<String> getIncludes() {
@@ -108,7 +109,7 @@ public class DefaultConfigurableFileTree extends CompositeFileTree implements Co
         return this;
     }
 
-    public DefaultConfigurableFileTree include(String ... includes) {
+    public DefaultConfigurableFileTree include(String... includes) {
         patternSet.include(includes);
         return this;
     }
@@ -128,7 +129,7 @@ public class DefaultConfigurableFileTree extends CompositeFileTree implements Co
         return this;
     }
 
-    public DefaultConfigurableFileTree exclude(String ... excludes) {
+    public DefaultConfigurableFileTree exclude(String... excludes) {
         patternSet.exclude(excludes);
         return this;
     }
diff --git a/subprojects/core/src/main/groovy/org/gradle/api/internal/file/collections/DelegatingFileCollection.java b/subprojects/core/src/main/groovy/org/gradle/api/internal/file/collections/DelegatingFileCollection.java
new file mode 100644
index 0000000..ac291ce
--- /dev/null
+++ b/subprojects/core/src/main/groovy/org/gradle/api/internal/file/collections/DelegatingFileCollection.java
@@ -0,0 +1,111 @@
+/*
+ * Copyright 2013 the original author or authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY 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.specs.Spec;
+import org.gradle.api.tasks.StopExecutionException;
+import org.gradle.api.tasks.TaskDependency;
+
+import java.io.File;
+import java.util.Iterator;
+import java.util.Set;
+
+/**
+ * A file collection that delegates each method call to the
+ * file collection returned by {@link #getDelegate()}.
+ */
+public abstract class DelegatingFileCollection implements FileCollection, MinimalFileSet {
+    public abstract FileCollection getDelegate();
+
+    public File getSingleFile() throws IllegalStateException {
+        return getDelegate().getSingleFile();
+    }
+
+    public Set<File> getFiles() {
+        return getDelegate().getFiles();
+    }
+
+    public boolean contains(File file) {
+        return getDelegate().contains(file);
+    }
+
+    public String getAsPath() {
+        return getDelegate().getAsPath();
+    }
+
+    public FileCollection plus(FileCollection collection) {
+        return getDelegate().plus(collection);
+    }
+
+    public FileCollection minus(FileCollection collection) {
+        return getDelegate().minus(collection);
+    }
+
+    public FileCollection filter(Closure filterClosure) {
+        return getDelegate().filter(filterClosure);
+    }
+
+    public FileCollection filter(Spec<? super File> filterSpec) {
+        return getDelegate().filter(filterSpec);
+    }
+
+    public Object asType(Class<?> type) throws UnsupportedOperationException {
+        return getDelegate().asType(type);
+    }
+
+    public FileCollection add(FileCollection collection) throws UnsupportedOperationException {
+        return getDelegate().add(collection);
+    }
+
+    public boolean isEmpty() {
+        return getDelegate().isEmpty();
+    }
+
+    public FileCollection stopExecutionIfEmpty() throws StopExecutionException {
+        return getDelegate().stopExecutionIfEmpty();
+    }
+
+    public FileTree getAsFileTree() {
+        return getDelegate().getAsFileTree();
+    }
+
+    public void addToAntBuilder(Object builder, String nodeName, AntType type) {
+        getDelegate().addToAntBuilder(builder, nodeName, type);
+    }
+
+    public Object addToAntBuilder(Object builder, String nodeName) {
+        return getDelegate().addToAntBuilder(builder, nodeName);
+    }
+
+    public TaskDependency getBuildDependencies() {
+        return getDelegate().getBuildDependencies();
+    }
+
+    public Iterator<File> iterator() {
+        return getDelegate().iterator();
+    }
+
+    public String getDisplayName() {
+        FileCollection delegate = getDelegate();
+        if (delegate instanceof MinimalFileSet) {
+            return ((MinimalFileSet) delegate).getDisplayName();
+        }
+        return getDelegate().toString();
+    }
+}
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
index 157cb4e..8922388 100644
--- 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
@@ -43,8 +43,6 @@ import java.util.regex.Pattern;
  *
  * 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);
@@ -106,7 +104,7 @@ public class DirectoryFileTree implements MinimalFileTree, PatternFilterableFile
 
     /**
      * 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
+     * may be either a directory or a file.  If it is a directory, then its 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.
      */
diff --git a/subprojects/core/src/main/groovy/org/gradle/api/internal/file/collections/LazilyInitializedFileCollection.java b/subprojects/core/src/main/groovy/org/gradle/api/internal/file/collections/LazilyInitializedFileCollection.java
new file mode 100644
index 0000000..cef9e92
--- /dev/null
+++ b/subprojects/core/src/main/groovy/org/gradle/api/internal/file/collections/LazilyInitializedFileCollection.java
@@ -0,0 +1,35 @@
+/*
+ * Copyright 2013 the original author or authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY 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;
+
+/**
+ * A {@link DelegatingFileCollection} whose delegate is created lazily.
+ */
+public abstract class LazilyInitializedFileCollection extends DelegatingFileCollection {
+    private FileCollection delegate;
+
+    public abstract FileCollection createDelegate();
+
+    @Override
+    public final synchronized FileCollection getDelegate() {
+        if (delegate == null) {
+            delegate = createDelegate();
+        }
+        return delegate;
+    }
+}
diff --git a/subprojects/core/src/main/groovy/org/gradle/api/internal/file/collections/SingleIncludePatternFileTree.java b/subprojects/core/src/main/groovy/org/gradle/api/internal/file/collections/SingleIncludePatternFileTree.java
index 04daa6e..f9d047d 100644
--- a/subprojects/core/src/main/groovy/org/gradle/api/internal/file/collections/SingleIncludePatternFileTree.java
+++ b/subprojects/core/src/main/groovy/org/gradle/api/internal/file/collections/SingleIncludePatternFileTree.java
@@ -59,7 +59,6 @@ public class SingleIncludePatternFileTree implements MinimalFileTree {
         this.excludeSpec = excludeSpec;
     }
 
-    // TODO: important to visit files in prefix order? (contract says breadth-wise but probably means prefix.)
     public void visit(FileVisitor visitor) {
         doVisit(visitor, baseDir, new LinkedList<String>(), 0, new AtomicBoolean());
     }
diff --git a/subprojects/core/src/main/groovy/org/gradle/api/internal/file/copy/AbstractZipCompressor.java b/subprojects/core/src/main/groovy/org/gradle/api/internal/file/copy/AbstractZipCompressor.java
index 4d1667a..8dc5cda 100644
--- a/subprojects/core/src/main/groovy/org/gradle/api/internal/file/copy/AbstractZipCompressor.java
+++ b/subprojects/core/src/main/groovy/org/gradle/api/internal/file/copy/AbstractZipCompressor.java
@@ -15,6 +15,7 @@
  */
 package org.gradle.api.internal.file.copy;
 
+import org.apache.tools.zip.Zip64Mode;
 import org.apache.tools.zip.ZipOutputStream;
 import org.gradle.api.UncheckedIOException;
 
@@ -27,6 +28,7 @@ abstract class AbstractZipCompressor implements ZipCompressor {
     public ZipOutputStream createArchiveOutputStream(File destination) {
         try {
             ZipOutputStream outStream = new ZipOutputStream(destination);
+            outStream.setUseZip64(Zip64Mode.Never);
             outStream.setMethod(getCompressedMethod());
             return outStream;
         } catch (Exception e) {
diff --git a/subprojects/core/src/main/groovy/org/gradle/api/internal/file/copy/ArchiveCopyAction.java b/subprojects/core/src/main/groovy/org/gradle/api/internal/file/copy/ArchiveCopyAction.java
deleted file mode 100644
index bccdf42..0000000
--- a/subprojects/core/src/main/groovy/org/gradle/api/internal/file/copy/ArchiveCopyAction.java
+++ /dev/null
@@ -1,25 +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.copy;
-
-import java.io.File;
-
-import org.gradle.api.internal.file.archive.compression.ArchiveOutputStreamFactory;
-
-public interface ArchiveCopyAction extends CopyAction {
-    File getArchivePath();
-    ArchiveOutputStreamFactory getCompressor();
-}
diff --git a/subprojects/core/src/main/groovy/org/gradle/api/internal/file/copy/CopyAction.java b/subprojects/core/src/main/groovy/org/gradle/api/internal/file/copy/CopyAction.java
index 5c3a686..55bdcac 100644
--- a/subprojects/core/src/main/groovy/org/gradle/api/internal/file/copy/CopyAction.java
+++ b/subprojects/core/src/main/groovy/org/gradle/api/internal/file/copy/CopyAction.java
@@ -1,25 +1,24 @@
-/*
- * Copyright 2009 the original author or authors.
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- *      http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-package org.gradle.api.internal.file.copy;
-
-import org.gradle.api.file.CopySpec;
-import org.gradle.api.tasks.WorkResult;
-
-/**
- * @author Steve Appling
- */
-public interface CopyAction extends CopySpec, WorkResult {
-}
+/*
+ * 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.tasks.WorkResult;
+
+public interface CopyAction {
+
+    public WorkResult execute(CopyActionProcessingStream stream);
+
+}
diff --git a/subprojects/core/src/main/groovy/org/gradle/api/internal/file/copy/CopyActionExecuter.java b/subprojects/core/src/main/groovy/org/gradle/api/internal/file/copy/CopyActionExecuter.java
new file mode 100644
index 0000000..fecd33f
--- /dev/null
+++ b/subprojects/core/src/main/groovy/org/gradle/api/internal/file/copy/CopyActionExecuter.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.api.internal.file.copy;
+
+import org.gradle.api.tasks.WorkResult;
+import org.gradle.internal.nativeplatform.filesystem.FileSystem;
+import org.gradle.internal.reflect.Instantiator;
+
+public class CopyActionExecuter {
+
+    private final Instantiator instantiator;
+    private final FileSystem fileSystem;
+
+    public CopyActionExecuter(Instantiator instantiator, FileSystem fileSystem) {
+        this.instantiator = instantiator;
+        this.fileSystem = fileSystem;
+    }
+
+    public WorkResult execute(final CopySpecInternal spec, CopyAction action) {
+        final CopyAction effectiveVisitor = new DuplicateHandlingCopyActionDecorator(
+                new NormalizingCopyActionDecorator(action)
+        );
+
+        CopyActionProcessingStream processingStream = new CopySpecBackedCopyActionProcessingStream(spec, instantiator, fileSystem);
+        return effectiveVisitor.execute(processingStream);
+    }
+
+}
\ No newline at end of file
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 36bf5ce..b108479 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,5 +1,5 @@
 /*
- * Copyright 2010 the original author or authors.
+ * Copyright 2013 the original author or authors.
  *
  * Licensed under the Apache License, Version 2.0 (the "License");
  * you may not use this file except in compliance with the License.
@@ -13,233 +13,16 @@
  * 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 org.gradle.internal.nativeplatform.filesystem.FileSystems;
 
-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;
+package org.gradle.api.internal.file.copy;
 
 /**
- * @author Steve Appling
+ * DO NOT REMOVE.
+ *
+ * Prior to 1.8, Copy leaked this as a parameter type on one of its methods.
+ * Has to exist to maintain binary compatibility.
  */
-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), FileSystems.getDefault());
-    }
-
-    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 Integer getDirMode() {
-        return mainContent.getDirMode();
-    }
-
-    public CopyProcessingSpec setDirMode(Integer mode) {
-        mainContent.setDirMode(mode);
-        return this;
-    }
-
-    public CopySpec setExcludes(Iterable<String> excludes) {
-        mainContent.setExcludes(excludes);
-        return this;
-    }
-
-    public Integer getFileMode() {
-        return mainContent.getFileMode();
-    }
-
-    public CopyProcessingSpec setFileMode(Integer 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;
-    }
+ at SuppressWarnings("UnusedDeclaration")
+ at Deprecated
+public interface CopyActionImpl {
 }
diff --git a/subprojects/core/src/main/groovy/org/gradle/api/internal/file/copy/CopyActionProcessingStream.java b/subprojects/core/src/main/groovy/org/gradle/api/internal/file/copy/CopyActionProcessingStream.java
new file mode 100644
index 0000000..f94eab7
--- /dev/null
+++ b/subprojects/core/src/main/groovy/org/gradle/api/internal/file/copy/CopyActionProcessingStream.java
@@ -0,0 +1,25 @@
+/*
+ * Copyright 2013 the original author or authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY 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.internal.file.CopyActionProcessingStreamAction;
+
+public interface CopyActionProcessingStream {
+
+    void process(CopyActionProcessingStreamAction action);
+
+}
diff --git a/subprojects/core/src/main/groovy/org/gradle/api/internal/file/copy/CopySpecBackedCopyActionProcessingStream.java b/subprojects/core/src/main/groovy/org/gradle/api/internal/file/copy/CopySpecBackedCopyActionProcessingStream.java
new file mode 100644
index 0000000..0306ad3
--- /dev/null
+++ b/subprojects/core/src/main/groovy/org/gradle/api/internal/file/copy/CopySpecBackedCopyActionProcessingStream.java
@@ -0,0 +1,68 @@
+/*
+ * Copyright 2013 the original author or authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY 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.Action;
+import org.gradle.api.file.FileCopyDetails;
+import org.gradle.api.file.FileTree;
+import org.gradle.api.file.FileVisitDetails;
+import org.gradle.api.file.FileVisitor;
+import org.gradle.api.internal.file.CopyActionProcessingStreamAction;
+import org.gradle.internal.nativeplatform.filesystem.FileSystem;
+import org.gradle.internal.reflect.Instantiator;
+
+public class CopySpecBackedCopyActionProcessingStream implements CopyActionProcessingStream {
+
+    private final CopySpecInternal spec;
+    private final Instantiator instantiator;
+    private final FileSystem fileSystem;
+
+    public CopySpecBackedCopyActionProcessingStream(CopySpecInternal spec, Instantiator instantiator, FileSystem fileSystem) {
+        this.spec = spec;
+        this.instantiator = instantiator;
+        this.fileSystem = fileSystem;
+    }
+
+    public void process(final CopyActionProcessingStreamAction action) {
+        spec.walk(new Action<CopySpecInternal>() {
+            public void execute(final CopySpecInternal spec) {
+                FileTree source = spec.getSource();
+                source.visit(new FileVisitor() {
+                    public void visitDir(FileVisitDetails dirDetails) {
+                        visit(dirDetails);
+                    }
+
+                    public void visitFile(FileVisitDetails fileDetails) {
+                        visit(fileDetails);
+                    }
+
+                    private void visit(FileVisitDetails visitDetails) {
+                        DefaultFileCopyDetails details = instantiator.newInstance(DefaultFileCopyDetails.class, visitDetails, spec, fileSystem);
+                        for (Action<? super FileCopyDetails> action : spec.getAllCopyActions()) {
+                            action.execute(details);
+                            if (details.isExcluded()) {
+                                return;
+                            }
+                        }
+                        action.processFile(details);
+                    }
+                });
+            }
+        });
+
+    }
+}
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
deleted file mode 100644
index 6d0d428..0000000
--- a/subprojects/core/src/main/groovy/org/gradle/api/internal/file/copy/CopySpecImpl.java
+++ /dev/null
@@ -1,461 +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.copy;
-
-import groovy.lang.Closure;
-import org.gradle.api.Action;
-import org.gradle.api.file.*;
-import org.gradle.api.internal.ChainingTransformer;
-import org.gradle.api.internal.ClosureBackedAction;
-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 PathNotationParser<String> pathNotationParser;
-
-    private CopySpecImpl(FileResolver resolver, CopySpecImpl parentSpec) {
-        this.parentSpec = parentSpec;
-        this.resolver = resolver;
-        this.pathNotationParser = new PathNotationParser<String>();
-        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) {
-            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) {
-        return pathNotationParser.parseNotation(destDir);
-    }
-
-    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 Integer getDirMode() {
-        if (dirMode != null) {
-            return dirMode;
-        }
-        if (parentSpec != null) {
-            return parentSpec.getDirMode();
-        }
-        return null;
-    }
-
-    public Integer getFileMode() {
-        if (fileMode != null) {
-            return fileMode;
-        }
-        if (parentSpec != null) {
-            return parentSpec.getFileMode();
-        }
-        return null;
-    }
-
-    public CopyProcessingSpec setDirMode(Integer mode) {
-        dirMode = mode;
-        return this;
-    }
-
-    public CopyProcessingSpec setFileMode(Integer mode) {
-        fileMode = mode;
-        return this;
-    }
-
-    public CopySpec eachFile(Action<? super FileCopyDetails> action) {
-        actions.add(action);
-        return this;
-    }
-
-    public CopySpec eachFile(Closure closure) {
-        actions.add(new ClosureBackedAction<FileCopyDetails>(closure));
-        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 Integer getFileMode() {
-            return spec.getFileMode();
-        }
-
-        public Integer 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/CopySpecInternal.java b/subprojects/core/src/main/groovy/org/gradle/api/internal/file/copy/CopySpecInternal.java
new file mode 100644
index 0000000..58ab4eb
--- /dev/null
+++ b/subprojects/core/src/main/groovy/org/gradle/api/internal/file/copy/CopySpecInternal.java
@@ -0,0 +1,45 @@
+/*
+ * 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.Action;
+import org.gradle.api.file.CopySpec;
+import org.gradle.api.file.FileCopyDetails;
+import org.gradle.api.file.FileTree;
+import org.gradle.api.file.RelativePath;
+
+import java.util.Collection;
+
+public interface CopySpecInternal extends CopySpec {
+
+    RelativePath getDestPath();
+
+    FileTree getSource();
+
+    FileTree getAllSource();
+
+    boolean hasSource();
+
+    Collection<? extends Action<? super FileCopyDetails>> getAllCopyActions();
+
+    Iterable<CopySpecInternal> getChildren();
+
+    void walk(Action<? super CopySpecInternal> action);
+
+    DefaultCopySpec addChild();
+
+    DefaultCopySpec addFirst();
+}
diff --git a/subprojects/core/src/main/groovy/org/gradle/api/internal/file/copy/CopySpecSource.java b/subprojects/core/src/main/groovy/org/gradle/api/internal/file/copy/CopySpecSource.java
index 38f27a2..381b9a9 100644
--- a/subprojects/core/src/main/groovy/org/gradle/api/internal/file/copy/CopySpecSource.java
+++ b/subprojects/core/src/main/groovy/org/gradle/api/internal/file/copy/CopySpecSource.java
@@ -16,5 +16,5 @@
 package org.gradle.api.internal.file.copy;
 
 public interface CopySpecSource {
-    ReadableCopySpec getRootSpec();
+    CopySpecInternal getRootSpec();
 }
diff --git a/subprojects/core/src/main/groovy/org/gradle/api/internal/file/copy/CopySpecVisitor.java b/subprojects/core/src/main/groovy/org/gradle/api/internal/file/copy/CopySpecVisitor.java
deleted file mode 100644
index 46f2cf9..0000000
--- a/subprojects/core/src/main/groovy/org/gradle/api/internal/file/copy/CopySpecVisitor.java
+++ /dev/null
@@ -1,36 +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.copy;
-
-import org.gradle.api.file.FileVisitor;
-import org.gradle.api.tasks.WorkResult;
-
-public interface CopySpecVisitor extends FileVisitor, WorkResult {
-    /**
-     * Called at the start of the visit.
-     */
-    void startVisit(CopyAction action);
-
-    /**
-     * Called at the end of the visit.
-     */
-    void endVisit();
-
-    /**
-     * Visits a spec. Called before any of the files or directories of the spec are visited.
-     */
-    void visitSpec(ReadableCopySpec spec);
-}
diff --git a/subprojects/core/src/main/groovy/org/gradle/api/internal/file/copy/DefaultCopySpec.java b/subprojects/core/src/main/groovy/org/gradle/api/internal/file/copy/DefaultCopySpec.java
new file mode 100644
index 0000000..33e9451
--- /dev/null
+++ b/subprojects/core/src/main/groovy/org/gradle/api/internal/file/copy/DefaultCopySpec.java
@@ -0,0 +1,462 @@
+/*
+ * Copyright 2010 the original author or authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY 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 com.google.common.collect.ImmutableList;
+import groovy.lang.Closure;
+import org.gradle.api.Action;
+import org.gradle.api.NonExtensible;
+import org.gradle.api.file.*;
+import org.gradle.api.internal.ChainingTransformer;
+import org.gradle.api.internal.ClosureBackedAction;
+import org.gradle.api.internal.file.FileResolver;
+import org.gradle.api.internal.file.pattern.PatternMatcherFactory;
+import org.gradle.api.specs.NotSpec;
+import org.gradle.api.specs.Spec;
+import org.gradle.api.tasks.util.PatternSet;
+import org.gradle.internal.reflect.Instantiator;
+import org.gradle.util.ConfigureUtil;
+
+import java.io.File;
+import java.io.FilterReader;
+import java.util.*;
+import java.util.regex.Pattern;
+
+ at NonExtensible
+public class DefaultCopySpec implements CopySpecInternal {
+    private final FileResolver resolver;
+    private final Set<Object> sourcePaths;
+    private Object destDir;
+    private final PatternSet patternSet;
+    private final List<CopySpecInternal> childSpecs;
+    private final Instantiator instantiator;
+    private final DefaultCopySpec 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 PathNotationParser<String> pathNotationParser;
+    private DuplicatesStrategy duplicatesStrategy;
+
+    public DefaultCopySpec(FileResolver resolver, Instantiator instantiator, DefaultCopySpec parentSpec) {
+        this.parentSpec = parentSpec;
+        this.resolver = resolver;
+        this.instantiator = instantiator;
+        this.pathNotationParser = new PathNotationParser<String>();
+        sourcePaths = new LinkedHashSet<Object>();
+        childSpecs = new ArrayList<CopySpecInternal>();
+        patternSet = new PatternSet();
+        duplicatesStrategy = null; //inherit from parent
+    }
+
+    public DefaultCopySpec(FileResolver resolver, Instantiator instantiator) {
+        this(resolver, instantiator, null);
+    }
+
+    protected FileResolver getResolver() {
+        return resolver;
+    }
+
+    public CopySpec with(CopySpec... copySpecs) {
+        for (CopySpec copySpec : copySpecs) {
+            CopySpecInternal copySpecInternal;
+            if (copySpec instanceof CopySpecSource) {
+                CopySpecSource copySpecSource = (CopySpecSource) copySpec;
+                copySpecInternal = copySpecSource.getRootSpec();
+            } else {
+                copySpecInternal = (CopySpecInternal) copySpec;
+            }
+            childSpecs.add(new RelativizedCopySpec(this, copySpecInternal));
+        }
+        return this;
+    }
+
+    public CopySpec from(Object... sourcePaths) {
+        for (Object sourcePath : sourcePaths) {
+            this.sourcePaths.add(sourcePath);
+        }
+        return this;
+    }
+
+    public CopySpec from(Object sourcePath, Closure c) {
+        if (c == null) {
+            from(sourcePath);
+            return this;
+        } else {
+            DefaultCopySpec child = addChild();
+            child.from(sourcePath);
+            ConfigureUtil.configure(c, child);
+            return child;
+        }
+    }
+
+    public DefaultCopySpec addFirst() {
+        DefaultCopySpec child = instantiator.newInstance(DefaultCopySpec.class, resolver, instantiator, this);
+        childSpecs.add(0, child);
+        return child;
+    }
+
+    public DefaultCopySpec addChild() {
+        DefaultCopySpec child = instantiator.newInstance(DefaultCopySpec.class, resolver, instantiator, this);
+        childSpecs.add(child);
+        return child;
+    }
+
+    public Set<Object> getSourcePaths() {
+        return sourcePaths;
+    }
+
+    public FileTree getSource() {
+        return resolver.resolveFilesAsTree(sourcePaths).matching(getPatternSet());
+    }
+
+    public FileTree getAllSource() {
+        final ImmutableList.Builder<FileTree> builder = ImmutableList.builder();
+        walk(new Action<CopySpecInternal>() {
+            public void execute(CopySpecInternal copySpecInternal) {
+                builder.add(copySpecInternal.getSource());
+            }
+        });
+
+        return resolver.compositeFileTree(builder.build());
+    }
+
+    public DefaultCopySpec into(Object destDir) {
+        this.destDir = destDir;
+        return this;
+    }
+
+    public DefaultCopySpec into(Object destPath, Closure configureClosure) {
+        if (configureClosure == null) {
+            into(destPath);
+            return this;
+        } else {
+            DefaultCopySpec 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) {
+        return pathNotationParser.parseNotation(destDir);
+    }
+
+    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 DuplicatesStrategy getDuplicatesStrategy() {
+        if (duplicatesStrategy != null) {
+            return duplicatesStrategy;
+        }
+        if (parentSpec != null) {
+            return parentSpec.getDuplicatesStrategy();
+        }
+        return DuplicatesStrategy.INCLUDE;
+    }
+
+    public void setDuplicatesStrategy(DuplicatesStrategy strategy) {
+        this.duplicatesStrategy = strategy;
+    }
+
+    public CopySpec filesMatching(String pattern, Action<? super FileCopyDetails> action) {
+        Spec<RelativePath> matcher = PatternMatcherFactory.getPatternMatcher(true, isCaseSensitive(), pattern);
+        return eachFile(
+                new MatchingCopyAction(matcher, action));
+    }
+
+    public CopySpec filesNotMatching(String pattern, Action<? super FileCopyDetails> action) {
+        Spec<RelativePath> matcher = PatternMatcherFactory.getPatternMatcher(true, isCaseSensitive(), pattern);
+        return eachFile(
+                new MatchingCopyAction(new NotSpec<RelativePath>(matcher), action));
+    }
+
+    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 DefaultCopySpec 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 Integer getDirMode() {
+        if (dirMode != null) {
+            return dirMode;
+        }
+        if (parentSpec != null) {
+            return parentSpec.getDirMode();
+        }
+        return null;
+    }
+
+    public Integer getFileMode() {
+        if (fileMode != null) {
+            return fileMode;
+        }
+        if (parentSpec != null) {
+            return parentSpec.getFileMode();
+        }
+        return null;
+    }
+
+    public CopyProcessingSpec setDirMode(Integer mode) {
+        dirMode = mode;
+        return this;
+    }
+
+    public CopyProcessingSpec setFileMode(Integer mode) {
+        fileMode = mode;
+        return this;
+    }
+
+    public CopySpec eachFile(Action<? super FileCopyDetails> action) {
+        actions.add(action);
+        return this;
+    }
+
+    public CopySpec eachFile(Closure closure) {
+        actions.add(new ClosureBackedAction<FileCopyDetails>(closure));
+        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 Iterable<CopySpecInternal> getChildren() {
+        return childSpecs;
+    }
+
+    public void walk(Action<? super CopySpecInternal> action) {
+        action.execute(this);
+        for (CopySpecInternal child : getChildren()) {
+            child.walk(action);
+        }
+    }
+
+    public boolean hasSource() {
+        if (!sourcePaths.isEmpty()) {
+            return true;
+        }
+        for (CopySpecInternal spec : childSpecs) {
+            if (spec.hasSource()) {
+                return true;
+            }
+        }
+        return false;
+    }
+
+}
diff --git a/subprojects/core/src/main/groovy/org/gradle/api/internal/file/copy/DefaultFileCopyDetails.java b/subprojects/core/src/main/groovy/org/gradle/api/internal/file/copy/DefaultFileCopyDetails.java
new file mode 100644
index 0000000..9c7b1d1
--- /dev/null
+++ b/subprojects/core/src/main/groovy/org/gradle/api/internal/file/copy/DefaultFileCopyDetails.java
@@ -0,0 +1,218 @@
+/*
+ * Copyright 2013 the original author or authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY 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.GradleException;
+import org.gradle.api.file.ContentFilterable;
+import org.gradle.api.file.DuplicatesStrategy;
+import org.gradle.api.file.FileVisitDetails;
+import org.gradle.api.file.RelativePath;
+import org.gradle.api.internal.file.AbstractFileTreeElement;
+import org.gradle.internal.nativeplatform.filesystem.FileSystem;
+
+import java.io.*;
+import java.util.Map;
+
+public class DefaultFileCopyDetails extends AbstractFileTreeElement implements FileVisitDetails, FileCopyDetailsInternal {
+    private final FileVisitDetails fileDetails;
+    private final CopySpecInternal spec;
+    private FileSystem fileSystem;
+    private final FilterChain filterChain = new FilterChain();
+    private RelativePath relativePath;
+    private boolean excluded;
+    private Integer mode;
+    private DuplicatesStrategy duplicatesStrategy;
+
+    public DefaultFileCopyDetails(FileVisitDetails fileDetails, CopySpecInternal spec, FileSystem fileSystem) {
+        this.fileDetails = fileDetails;
+        this.spec = spec;
+        this.fileSystem = fileSystem;
+        this.duplicatesStrategy = spec.getDuplicatesStrategy();
+    }
+
+    public boolean isIncludeEmptyDirs() {
+        return spec.getIncludeEmptyDirs();
+    }
+
+    public String getDisplayName() {
+        return fileDetails.toString();
+    }
+
+    public void stopVisiting() {
+        fileDetails.stopVisiting();
+    }
+
+    public File getFile() {
+        if (filterChain.hasFilters()) {
+            throw new UnsupportedOperationException();
+        } else {
+            return fileDetails.getFile();
+        }
+    }
+
+    public boolean isDirectory() {
+        return fileDetails.isDirectory();
+    }
+
+    public long getLastModified() {
+        return fileDetails.getLastModified();
+    }
+
+    public long getSize() {
+        if (filterChain.hasFilters()) {
+            ByteCountingOutputStream outputStream = new ByteCountingOutputStream();
+            copyTo(outputStream);
+            return outputStream.size;
+        } else {
+            return fileDetails.getSize();
+        }
+    }
+
+    public InputStream open() {
+        if (filterChain.hasFilters()) {
+            return filterChain.transform(fileDetails.open());
+        } else {
+            return fileDetails.open();
+        }
+    }
+
+    public void copyTo(OutputStream outstr) {
+        if (filterChain.hasFilters()) {
+            super.copyTo(outstr);
+        } else {
+            fileDetails.copyTo(outstr);
+        }
+    }
+
+    public boolean copyTo(File target) {
+        if (filterChain.hasFilters()) {
+            return super.copyTo(target);
+        } else {
+            final boolean copied = fileDetails.copyTo(target);
+            adaptPermissions(target);
+            return copied;
+        }
+    }
+
+    private void adaptPermissions(File target) {
+        final Integer specMode = getMode();
+        if(specMode !=null){
+            try {
+                fileSystem.chmod(target, specMode);
+            } catch (IOException e) {
+                throw new GradleException(String.format("Could not set permission %s on '%s'.", specMode, target), e);
+            }
+        }
+    }
+
+    public RelativePath getRelativePath() {
+        if (relativePath == null) {
+            RelativePath path = fileDetails.getRelativePath();
+            relativePath = spec.getDestPath().append(path.isFile(), path.getSegments());
+        }
+        return relativePath;
+    }
+
+    public int getMode() {
+        if (mode != null) {
+            return mode;
+        }
+
+        Integer specMode = getSpecMode();
+        if (specMode != null) {
+            return specMode;
+        }
+
+        return fileDetails.getMode();
+    }
+
+    private Integer getSpecMode() {
+        return fileDetails.isDirectory() ? spec.getDirMode() : spec.getFileMode();
+    }
+
+    public void setRelativePath(RelativePath path) {
+        this.relativePath = path;
+    }
+
+    public void setName(String name) {
+        relativePath = getRelativePath().replaceLastName(name);
+    }
+
+    public void setPath(String path) {
+        relativePath = RelativePath.parse(getRelativePath().isFile(), path);
+    }
+
+    boolean isExcluded() {
+        return excluded;
+    }
+
+    public void exclude() {
+        excluded = true;
+    }
+
+    public void setMode(int mode) {
+        this.mode = mode;
+    }
+
+    public ContentFilterable filter(Closure closure) {
+        filterChain.add(closure);
+        return this;
+    }
+
+    public ContentFilterable filter(Map<String, ?> properties, Class<? extends FilterReader> filterType) {
+        filterChain.add(filterType, properties);
+        return this;
+    }
+
+    public ContentFilterable filter(Class<? extends FilterReader> filterType) {
+        filterChain.add(filterType);
+        return this;
+    }
+
+    public ContentFilterable expand(Map<String, ?> properties) {
+        filterChain.expand(properties);
+        return this;
+    }
+
+    public void setDuplicatesStrategy(DuplicatesStrategy strategy) {
+        this.duplicatesStrategy = strategy;
+    }
+
+    public DuplicatesStrategy getDuplicatesStrategy() {
+        return this.duplicatesStrategy;
+    }
+
+    private static class ByteCountingOutputStream extends OutputStream {
+        long size;
+
+        @Override
+        public void write(int b) throws IOException {
+            size++;
+        }
+
+        @Override
+        public void write(byte[] b) throws IOException {
+            size += b.length;
+        }
+
+        @Override
+        public void write(byte[] b, int off, int len) throws IOException {
+            size += len;
+        }
+    }
+}
diff --git a/subprojects/core/src/main/groovy/org/gradle/api/internal/file/copy/DelegatingCopySpec.java b/subprojects/core/src/main/groovy/org/gradle/api/internal/file/copy/DelegatingCopySpec.java
new file mode 100644
index 0000000..fc924d7
--- /dev/null
+++ b/subprojects/core/src/main/groovy/org/gradle/api/internal/file/copy/DelegatingCopySpec.java
@@ -0,0 +1,224 @@
+/*
+ * Copyright 2013 the original author or authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY 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.specs.Spec;
+
+import java.io.FilterReader;
+import java.util.Collection;
+import java.util.Map;
+import java.util.Set;
+import java.util.regex.Pattern;
+
+abstract public class DelegatingCopySpec implements CopySpecInternal {
+
+    abstract protected CopySpecInternal getDelegateCopySpec();
+
+    public RelativePath getDestPath() {
+        return getDelegateCopySpec().getDestPath();
+    }
+
+    public FileTree getSource() {
+        return getDelegateCopySpec().getSource();
+    }
+
+    public boolean hasSource() {
+        return getDelegateCopySpec().hasSource();
+    }
+
+    public Collection<? extends Action<? super FileCopyDetails>> getAllCopyActions() {
+        return getDelegateCopySpec().getAllCopyActions();
+    }
+
+    public boolean isCaseSensitive() {
+        return getDelegateCopySpec().isCaseSensitive();
+    }
+
+    public void setCaseSensitive(boolean caseSensitive) {
+        getDelegateCopySpec().setCaseSensitive(caseSensitive);
+    }
+
+    public boolean getIncludeEmptyDirs() {
+        return getDelegateCopySpec().getIncludeEmptyDirs();
+    }
+
+    public void setIncludeEmptyDirs(boolean includeEmptyDirs) {
+        getDelegateCopySpec().setIncludeEmptyDirs(includeEmptyDirs);
+    }
+
+    public DuplicatesStrategy getDuplicatesStrategy() {
+        return getDelegateCopySpec().getDuplicatesStrategy();
+    }
+
+    public void setDuplicatesStrategy(DuplicatesStrategy strategy) {
+        getDelegateCopySpec().setDuplicatesStrategy(strategy);
+    }
+
+    public CopySpec filesMatching(String pattern, Action<? super FileCopyDetails> action) {
+        return getDelegateCopySpec().filesMatching(pattern, action);
+    }
+
+     public CopySpec filesNotMatching(String pattern, Action<? super FileCopyDetails> action) {
+        return getDelegateCopySpec().filesNotMatching(pattern, action);
+    }
+
+    public CopySpec with(CopySpec... sourceSpecs) {
+        return getDelegateCopySpec().with(sourceSpecs);
+    }
+
+    public CopySpec from(Object... sourcePaths) {
+        return getDelegateCopySpec().from(sourcePaths);
+    }
+
+    public CopySpec from(Object sourcePath, Closure c) {
+        return getDelegateCopySpec().from(sourcePath, c);
+    }
+
+    public CopySpec setIncludes(Iterable<String> includes) {
+        return getDelegateCopySpec().setIncludes(includes);
+    }
+
+    public CopySpec setExcludes(Iterable<String> excludes) {
+        return getDelegateCopySpec().setExcludes(excludes);
+    }
+
+    public CopySpec include(String... includes) {
+        return getDelegateCopySpec().include(includes);
+    }
+
+    public CopySpec include(Iterable<String> includes) {
+        return getDelegateCopySpec().include(includes);
+    }
+
+    public CopySpec include(Spec<FileTreeElement> includeSpec) {
+        return getDelegateCopySpec().include(includeSpec);
+    }
+
+    public CopySpec include(Closure includeSpec) {
+        return getDelegateCopySpec().include(includeSpec);
+    }
+
+    public CopySpec exclude(String... excludes) {
+        return getDelegateCopySpec().exclude(excludes);
+    }
+
+    public CopySpec exclude(Iterable<String> excludes) {
+        return getDelegateCopySpec().exclude(excludes);
+    }
+
+    public CopySpec exclude(Spec<FileTreeElement> excludeSpec) {
+        return getDelegateCopySpec().exclude(excludeSpec);
+    }
+
+    public CopySpec exclude(Closure excludeSpec) {
+        return getDelegateCopySpec().exclude(excludeSpec);
+    }
+
+    public CopySpec into(Object destPath) {
+        return getDelegateCopySpec().into(destPath);
+    }
+
+    public CopySpec into(Object destPath, Closure configureClosure) {
+        return getDelegateCopySpec().into(destPath, configureClosure);
+    }
+
+    public CopySpec rename(Closure closure) {
+        return getDelegateCopySpec().rename(closure);
+    }
+
+    public CopySpec rename(String sourceRegEx, String replaceWith) {
+        return getDelegateCopySpec().rename(sourceRegEx, replaceWith);
+    }
+
+    public CopyProcessingSpec rename(Pattern sourceRegEx, String replaceWith) {
+        return getDelegateCopySpec().rename(sourceRegEx, replaceWith);
+    }
+
+    public CopySpec filter(Map<String, ?> properties, Class<? extends FilterReader> filterType) {
+        return getDelegateCopySpec().filter(properties, filterType);
+    }
+
+    public CopySpec filter(Class<? extends FilterReader> filterType) {
+        return getDelegateCopySpec().filter(filterType);
+    }
+
+    public CopySpec filter(Closure closure) {
+        return getDelegateCopySpec().filter(closure);
+    }
+
+    public CopySpec expand(Map<String, ?> properties) {
+        return getDelegateCopySpec().expand(properties);
+    }
+
+    public CopySpec eachFile(Action<? super FileCopyDetails> action) {
+        return getDelegateCopySpec().eachFile(action);
+    }
+
+    public CopySpec eachFile(Closure closure) {
+        return getDelegateCopySpec().eachFile(closure);
+    }
+
+    public Integer getFileMode() {
+        return getDelegateCopySpec().getFileMode();
+    }
+
+    public CopyProcessingSpec setFileMode(Integer mode) {
+        return getDelegateCopySpec().setFileMode(mode);
+    }
+
+    public Integer getDirMode() {
+        return getDelegateCopySpec().getDirMode();
+    }
+
+    public CopyProcessingSpec setDirMode(Integer mode) {
+        return getDelegateCopySpec().setDirMode(mode);
+    }
+
+    public Set<String> getIncludes() {
+        return getDelegateCopySpec().getIncludes();
+    }
+
+    public Set<String> getExcludes() {
+        return getDelegateCopySpec().getExcludes();
+    }
+
+    public Iterable<CopySpecInternal> getChildren() {
+        return getDelegateCopySpec().getChildren();
+    }
+
+    public FileTree getAllSource() {
+        return getDelegateCopySpec().getAllSource();
+    }
+
+    public DefaultCopySpec addChild() {
+        return getDelegateCopySpec().addChild();
+    }
+
+    public DefaultCopySpec addFirst() {
+        return getDelegateCopySpec().addFirst();
+    }
+
+    public void walk(Action<? super CopySpecInternal> action) {
+        action.execute(this);
+        for (CopySpecInternal child : getChildren()) {
+            child.walk(action);
+        }
+    }
+}
diff --git a/subprojects/core/src/main/groovy/org/gradle/api/internal/file/copy/DelegatingCopySpecVisitor.java b/subprojects/core/src/main/groovy/org/gradle/api/internal/file/copy/DelegatingCopySpecVisitor.java
deleted file mode 100644
index 0a5ee3a..0000000
--- a/subprojects/core/src/main/groovy/org/gradle/api/internal/file/copy/DelegatingCopySpecVisitor.java
+++ /dev/null
@@ -1,54 +0,0 @@
-/*
- * Copyright 2009 the original author or authors.
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- *      http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-package org.gradle.api.internal.file.copy;
-
-import org.gradle.api.file.FileVisitDetails;
-
-public class DelegatingCopySpecVisitor implements CopySpecVisitor {
-    private final CopySpecVisitor visitor;
-
-    public DelegatingCopySpecVisitor(CopySpecVisitor visitor) {
-        this.visitor = visitor;
-    }
-
-    protected CopySpecVisitor getVisitor() {
-        return visitor;
-    }
-
-    public void startVisit(CopyAction action) {
-        getVisitor().startVisit(action);
-    }
-
-    public void endVisit() {
-        getVisitor().endVisit();
-    }
-
-    public void visitSpec(ReadableCopySpec spec) {
-        getVisitor().visitSpec(spec);
-    }
-
-    public void visitDir(FileVisitDetails dirDetails) {
-        getVisitor().visitDir(dirDetails);
-    }
-
-    public void visitFile(FileVisitDetails fileDetails) {
-        getVisitor().visitFile(fileDetails);
-    }
-
-    public boolean getDidWork() {
-        return getVisitor().getDidWork();
-    }
-}
diff --git a/subprojects/core/src/main/groovy/org/gradle/api/internal/file/copy/DeleteActionImpl.java b/subprojects/core/src/main/groovy/org/gradle/api/internal/file/copy/DeleteActionImpl.java
index 923fc72..f3c72c9 100644
--- a/subprojects/core/src/main/groovy/org/gradle/api/internal/file/copy/DeleteActionImpl.java
+++ b/subprojects/core/src/main/groovy/org/gradle/api/internal/file/copy/DeleteActionImpl.java
@@ -24,9 +24,6 @@ import org.slf4j.LoggerFactory;
 
 import java.io.File;
 
-/**
- * @author Hans Dockter
- */
 public class DeleteActionImpl implements DeleteAction {
     private static Logger logger = LoggerFactory.getLogger(DeleteActionImpl.class);
     
diff --git a/subprojects/core/src/main/groovy/org/gradle/api/internal/file/copy/DestinationRootCopySpec.java b/subprojects/core/src/main/groovy/org/gradle/api/internal/file/copy/DestinationRootCopySpec.java
new file mode 100644
index 0000000..d26fe2d
--- /dev/null
+++ b/subprojects/core/src/main/groovy/org/gradle/api/internal/file/copy/DestinationRootCopySpec.java
@@ -0,0 +1,51 @@
+/*
+ * Copyright 2013 the original author or authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.gradle.api.internal.file.copy;
+
+import org.gradle.api.file.CopySpec;
+import org.gradle.api.internal.file.FileResolver;
+
+import java.io.File;
+
+public class DestinationRootCopySpec extends DelegatingCopySpec {
+
+    private final FileResolver fileResolver;
+    private final CopySpecInternal delegate;
+
+    private Object destinationDir;
+
+    public DestinationRootCopySpec(FileResolver fileResolver, CopySpecInternal delegate) {
+        this.fileResolver = fileResolver;
+        this.delegate = delegate;
+    }
+
+    @Override
+    protected CopySpecInternal getDelegateCopySpec() {
+        return delegate;
+    }
+
+    @Override
+    public CopySpec into(Object destinationDir) {
+        this.destinationDir = destinationDir;
+        return this;
+    }
+
+    public File getDestinationDir() {
+        return destinationDir == null ? null : fileResolver.resolve(destinationDir);
+    }
+
+}
diff --git a/subprojects/core/src/main/groovy/org/gradle/api/internal/file/copy/DuplicateHandlingCopyActionDecorator.java b/subprojects/core/src/main/groovy/org/gradle/api/internal/file/copy/DuplicateHandlingCopyActionDecorator.java
new file mode 100644
index 0000000..d53a048
--- /dev/null
+++ b/subprojects/core/src/main/groovy/org/gradle/api/internal/file/copy/DuplicateHandlingCopyActionDecorator.java
@@ -0,0 +1,66 @@
+/*
+ * Copyright 2013 the original author or authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.gradle.api.internal.file.copy;
+
+import org.gradle.api.file.DuplicateFileCopyingException;
+import org.gradle.api.file.DuplicatesStrategy;
+import org.gradle.api.file.RelativePath;
+import org.gradle.api.internal.file.CopyActionProcessingStreamAction;
+import org.gradle.api.logging.Logger;
+import org.gradle.api.logging.Logging;
+import org.gradle.api.tasks.WorkResult;
+
+import java.util.HashSet;
+import java.util.Set;
+
+public class DuplicateHandlingCopyActionDecorator implements CopyAction {
+
+    private final static Logger LOGGER = Logging.getLogger(DuplicateHandlingCopyActionDecorator.class);
+    private final CopyAction delegate;
+
+    public DuplicateHandlingCopyActionDecorator(CopyAction delegate) {
+        this.delegate = delegate;
+    }
+
+    public WorkResult execute(final CopyActionProcessingStream stream) {
+        final Set<RelativePath> visitedFiles = new HashSet<RelativePath>();
+
+        return delegate.execute(new CopyActionProcessingStream() {
+            public void process(final CopyActionProcessingStreamAction action) {
+                stream.process(new CopyActionProcessingStreamAction() {
+                    public void processFile(FileCopyDetailsInternal details) {
+                        if (!details.isDirectory()) {
+                            DuplicatesStrategy strategy = details.getDuplicatesStrategy();
+
+                            if (!visitedFiles.add(details.getRelativePath())) {
+                                if (strategy == DuplicatesStrategy.EXCLUDE) {
+                                    return;
+                                } else if (strategy == DuplicatesStrategy.FAIL) {
+                                    throw new DuplicateFileCopyingException(String.format("Encountered duplicate path \"%s\" during copy operation configured with DuplicatesStrategy.FAIL", details.getRelativePath()));
+                                } else if (strategy == DuplicatesStrategy.WARN) {
+                                    LOGGER.warn("Encountered duplicate path \"{}\" during copy operation configured with DuplicatesStrategy.WARN", details.getRelativePath());
+                                }
+                            }
+                        }
+
+                        action.processFile(details);
+                    }
+                });
+            }
+        });
+    }
+}
diff --git a/subprojects/core/src/main/groovy/org/gradle/api/internal/file/copy/EmptyCopySpecVisitor.java b/subprojects/core/src/main/groovy/org/gradle/api/internal/file/copy/EmptyCopySpecVisitor.java
deleted file mode 100644
index 0c9dbce..0000000
--- a/subprojects/core/src/main/groovy/org/gradle/api/internal/file/copy/EmptyCopySpecVisitor.java
+++ /dev/null
@@ -1,39 +0,0 @@
-/*
- * Copyright 2009 the original author or authors.
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- *      http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-package org.gradle.api.internal.file.copy;
-
-import org.gradle.api.file.FileVisitDetails;
-
-public class EmptyCopySpecVisitor implements CopySpecVisitor {
-    public boolean getDidWork() {
-        return false;
-    }
-
-    public void startVisit(CopyAction action) {
-    }
-
-    public void visitDir(FileVisitDetails dirDetails) {
-    }
-
-    public void endVisit() {
-    }
-
-    public void visitFile(FileVisitDetails fileDetails) {
-    }
-
-    public void visitSpec(ReadableCopySpec spec) {
-    }
-}
diff --git a/subprojects/core/src/main/groovy/org/gradle/api/internal/file/copy/FileCopier.java b/subprojects/core/src/main/groovy/org/gradle/api/internal/file/copy/FileCopier.java
new file mode 100644
index 0000000..cb4c813
--- /dev/null
+++ b/subprojects/core/src/main/groovy/org/gradle/api/internal/file/copy/FileCopier.java
@@ -0,0 +1,66 @@
+/*
+ * Copyright 2009 the original author or authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.gradle.api.internal.file.copy;
+
+import org.gradle.api.Action;
+import org.gradle.api.file.CopySpec;
+import org.gradle.api.internal.file.BaseDirFileResolver;
+import org.gradle.api.internal.file.FileResolver;
+import org.gradle.api.tasks.WorkResult;
+import org.gradle.internal.nativeplatform.filesystem.FileSystems;
+import org.gradle.internal.reflect.Instantiator;
+
+import java.io.File;
+
+public class FileCopier {
+
+    private final Instantiator instantiator;
+    private final FileResolver fileResolver;
+
+    public FileCopier(Instantiator instantiator, FileResolver fileResolver) {
+        this.instantiator = instantiator;
+        this.fileResolver = fileResolver;
+    }
+
+    private DestinationRootCopySpec createCopySpec(Action<? super CopySpec> action) {
+        DefaultCopySpec copySpec = instantiator.newInstance(DefaultCopySpec.class, this.fileResolver, instantiator);
+        DestinationRootCopySpec destinationRootCopySpec = instantiator.newInstance(DestinationRootCopySpec.class, fileResolver, copySpec);
+        action.execute(destinationRootCopySpec);
+        return destinationRootCopySpec;
+    }
+
+    public WorkResult copy(Action<? super CopySpec> action) {
+        DestinationRootCopySpec copySpec = createCopySpec(action);
+        File destinationDir = copySpec.getDestinationDir();
+        return doCopy(copySpec, getCopyVisitor(destinationDir));
+    }
+
+    public WorkResult sync(Action<? super CopySpec> action) {
+        DestinationRootCopySpec copySpec = createCopySpec(action);
+        File destinationDir = copySpec.getDestinationDir();
+        return doCopy(copySpec, new SyncCopyActionDecorator(destinationDir, getCopyVisitor(destinationDir)));
+    }
+
+    private FileCopyAction getCopyVisitor(File destination) {
+        return new FileCopyAction(new BaseDirFileResolver(FileSystems.getDefault(), destination));
+    }
+
+    private WorkResult doCopy(CopySpecInternal copySpec, CopyAction visitor) {
+        CopyActionExecuter visitorDriver = new CopyActionExecuter(instantiator, FileSystems.getDefault());
+        return visitorDriver.execute(copySpec, visitor);
+    }
+
+}
diff --git a/subprojects/core/src/main/groovy/org/gradle/api/internal/file/copy/FileCopyAction.java b/subprojects/core/src/main/groovy/org/gradle/api/internal/file/copy/FileCopyAction.java
index 7040c9d..00e8b1c 100644
--- a/subprojects/core/src/main/groovy/org/gradle/api/internal/file/copy/FileCopyAction.java
+++ b/subprojects/core/src/main/groovy/org/gradle/api/internal/file/copy/FileCopyAction.java
@@ -15,8 +15,36 @@
  */
 package org.gradle.api.internal.file.copy;
 
+import org.gradle.api.internal.file.CopyActionProcessingStreamAction;
+import org.gradle.api.internal.file.FileResolver;
+import org.gradle.api.internal.tasks.SimpleWorkResult;
+import org.gradle.api.tasks.WorkResult;
+
 import java.io.File;
 
-public interface FileCopyAction extends CopyAction {
-    File getDestinationDir();
+public class FileCopyAction implements CopyAction {
+
+    private final FileResolver fileResolver;
+
+    public FileCopyAction(FileResolver fileResolver) {
+        this.fileResolver = fileResolver;
+    }
+
+    public WorkResult execute(CopyActionProcessingStream stream) {
+        FileCopyDetailsInternalAction action = new FileCopyDetailsInternalAction();
+        stream.process(action);
+        return new SimpleWorkResult(action.didWork);
+    }
+
+    private class FileCopyDetailsInternalAction implements CopyActionProcessingStreamAction {
+        private boolean didWork;
+
+        public void processFile(FileCopyDetailsInternal details) {
+            File target = fileResolver.resolve(details.getRelativePath().getPathString());
+            boolean copied = details.copyTo(target);
+            if (copied) {
+                didWork = true;
+            }
+        }
+    }
 }
diff --git a/subprojects/core/src/main/groovy/org/gradle/api/internal/file/copy/FileCopyActionImpl.java b/subprojects/core/src/main/groovy/org/gradle/api/internal/file/copy/FileCopyActionImpl.java
index 1a35e8c..659fc1e 100644
--- a/subprojects/core/src/main/groovy/org/gradle/api/internal/file/copy/FileCopyActionImpl.java
+++ b/subprojects/core/src/main/groovy/org/gradle/api/internal/file/copy/FileCopyActionImpl.java
@@ -1,5 +1,5 @@
 /*
- * Copyright 2009 the original author or authors.
+ * Copyright 2013 the original author or authors.
  *
  * Licensed under the Apache License, Version 2.0 (the "License");
  * you may not use this file except in compliance with the License.
@@ -13,27 +13,16 @@
  * See the License for the specific language governing permissions and
  * limitations under the License.
  */
-package org.gradle.api.internal.file.copy;
-
-import org.gradle.api.file.CopySpec;
-import org.gradle.api.internal.file.FileResolver;
-
-import java.io.File;
-
-public class FileCopyActionImpl extends CopyActionImpl implements FileCopyAction {
-    private Object destDir;
 
-    public FileCopyActionImpl(FileResolver resolver, CopySpecVisitor visitor) {
-        super(resolver, visitor);
-    }
-
-    @Override
-    public CopySpec into(Object destDir) {
-        this.destDir = destDir;
-        return this;
-    }
+package org.gradle.api.internal.file.copy;
 
-    public File getDestinationDir() {
-        return destDir == null ? null : getResolver().resolve(destDir);
-    }
+/**
+ * DO NOT REMOVE.
+ *
+ * Prior to 1.8, Copy leaked this as a parameter type on one of its methods.
+ * Has to exist to maintain binary compatibility.
+ */
+ at SuppressWarnings("UnusedDeclaration")
+ at Deprecated
+public class FileCopyActionImpl {
 }
diff --git a/subprojects/core/src/main/groovy/org/gradle/api/internal/file/copy/FileCopyDetailsInternal.java b/subprojects/core/src/main/groovy/org/gradle/api/internal/file/copy/FileCopyDetailsInternal.java
new file mode 100644
index 0000000..8de9bfd
--- /dev/null
+++ b/subprojects/core/src/main/groovy/org/gradle/api/internal/file/copy/FileCopyDetailsInternal.java
@@ -0,0 +1,25 @@
+/*
+ * Copyright 2013 the original author or authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.gradle.api.internal.file.copy;
+
+import org.gradle.api.file.FileCopyDetails;
+
+public interface FileCopyDetailsInternal extends FileCopyDetails {
+
+    boolean isIncludeEmptyDirs();
+
+}
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
deleted file mode 100644
index 712e986..0000000
--- a/subprojects/core/src/main/groovy/org/gradle/api/internal/file/copy/FileCopySpecVisitor.java
+++ /dev/null
@@ -1,61 +0,0 @@
-/*
- * Copyright 2009 the original author or authors.
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- *      http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-package org.gradle.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/MappingCopySpecVisitor.java b/subprojects/core/src/main/groovy/org/gradle/api/internal/file/copy/MappingCopySpecVisitor.java
deleted file mode 100644
index a662c9d..0000000
--- a/subprojects/core/src/main/groovy/org/gradle/api/internal/file/copy/MappingCopySpecVisitor.java
+++ /dev/null
@@ -1,230 +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.copy;
-
-import groovy.lang.Closure;
-import org.gradle.api.Action;
-import org.gradle.api.GradleException;
-import org.gradle.api.file.ContentFilterable;
-import org.gradle.api.file.FileCopyDetails;
-import org.gradle.api.file.FileVisitDetails;
-import org.gradle.api.file.RelativePath;
-import org.gradle.api.internal.file.AbstractFileTreeElement;
-import org.gradle.internal.nativeplatform.filesystem.FileSystem;
-
-import java.io.*;
-import java.util.Map;
-
-public class MappingCopySpecVisitor extends DelegatingCopySpecVisitor {
-    private ReadableCopySpec spec;
-    private FileSystem fileSystem;
-
-    public MappingCopySpecVisitor(CopySpecVisitor visitor, FileSystem fileSystem) {
-        super(visitor);
-        this.fileSystem = fileSystem;
-    }
-
-    public void visitSpec(ReadableCopySpec spec) {
-        this.spec = spec;
-        getVisitor().visitSpec(spec);
-    }
-
-    public void visitDir(FileVisitDetails dirDetails) {
-        getVisitor().visitDir(new FileVisitDetailsImpl(dirDetails, spec, fileSystem));
-    }
-
-    public void visitFile(final FileVisitDetails fileDetails) {
-        FileVisitDetailsImpl details = new FileVisitDetailsImpl(fileDetails, spec, fileSystem);
-        for (Action<? super FileCopyDetails> action : spec.getAllCopyActions()) {
-            action.execute(details);
-            if (details.excluded) {
-                return;
-            }
-        }
-        getVisitor().visitFile(details);
-    }
-
-    private static class FileVisitDetailsImpl extends AbstractFileTreeElement implements FileVisitDetails, FileCopyDetails {
-        private final FileVisitDetails fileDetails;
-        private final ReadableCopySpec spec;
-        private FileSystem fileSystem;
-        private final FilterChain filterChain = new FilterChain();
-        private RelativePath relativePath;
-        private boolean excluded;
-        private Integer mode;
-
-        public FileVisitDetailsImpl(FileVisitDetails fileDetails, ReadableCopySpec spec, FileSystem fileSystem) {
-            this.fileDetails = fileDetails;
-            this.spec = spec;
-            this.fileSystem = fileSystem;
-        }
-
-        public String getDisplayName() {
-            return fileDetails.toString();
-        }
-
-        public void stopVisiting() {
-            fileDetails.stopVisiting();
-        }
-
-        public File getFile() {
-            if (filterChain.hasFilters()) {
-                throw new UnsupportedOperationException();
-            } else {
-                return fileDetails.getFile();
-            }
-        }
-
-        public boolean isDirectory() {
-            return fileDetails.isDirectory();
-        }
-
-        public long getLastModified() {
-            return fileDetails.getLastModified();
-        }
-
-        public long getSize() {
-            if (filterChain.hasFilters()) {
-                ByteCountingOutputStream outputStream = new ByteCountingOutputStream();
-                copyTo(outputStream);
-                return outputStream.size;
-            } else {
-                return fileDetails.getSize();
-            }
-        }
-
-        public InputStream open() {
-            if (filterChain.hasFilters()) {
-                return filterChain.transform(fileDetails.open());
-            } else {
-                return fileDetails.open();
-            }
-        }
-
-        public void copyTo(OutputStream outstr) {
-            if (filterChain.hasFilters()) {
-                super.copyTo(outstr);
-            } else {
-                fileDetails.copyTo(outstr);
-            }
-        }
-
-        public boolean copyTo(File target) {
-            if (filterChain.hasFilters()) {
-                return super.copyTo(target);
-            } else {
-                final boolean copied = fileDetails.copyTo(target);
-                adaptPermissions(target);
-                return copied;
-            }
-        }
-
-        private void adaptPermissions(File target) {
-            final Integer specMode = getMode();
-            if(specMode !=null){
-                try {
-                    fileSystem.chmod(target, specMode);
-                } catch (IOException e) {
-                    throw new GradleException(String.format("Could not set permission %s on '%s'.", specMode, target), e);
-                }
-            }
-        }
-
-        public RelativePath getRelativePath() {
-            if (relativePath == null) {
-                RelativePath path = fileDetails.getRelativePath();
-                relativePath = spec.getDestPath().append(path.isFile(), path.getSegments());
-            }
-            return relativePath;
-        }
-
-        public int getMode() {
-            if (mode != null) {
-                return mode;
-            }
-
-            Integer specMode = getSpecMode();
-            if (specMode != null) {
-                return specMode;
-            }
-
-            return fileDetails.getMode();
-        }
-
-        private Integer getSpecMode() {
-            return fileDetails.isDirectory() ? spec.getDirMode() : spec.getFileMode();
-        }
-
-        public void setRelativePath(RelativePath path) {
-            this.relativePath = path;
-        }
-
-        public void setName(String name) {
-            relativePath = getRelativePath().replaceLastName(name);
-        }
-
-        public void setPath(String path) {
-            relativePath = RelativePath.parse(getRelativePath().isFile(), path);
-        }
-
-        public void exclude() {
-            excluded = true;
-        }
-
-        public void setMode(int mode) {
-            this.mode = mode;
-        }
-
-        public ContentFilterable filter(Closure closure) {
-            filterChain.add(closure);
-            return this;
-        }
-
-        public ContentFilterable filter(Map<String, ?> properties, Class<? extends FilterReader> filterType) {
-            filterChain.add(filterType, properties);
-            return this;
-        }
-
-        public ContentFilterable filter(Class<? extends FilterReader> filterType) {
-            filterChain.add(filterType);
-            return this;
-        }
-
-        public ContentFilterable expand(Map<String, ?> properties) {
-            filterChain.expand(properties);
-            return this;
-        }
-    }
-
-    private static class ByteCountingOutputStream extends OutputStream {
-        long size;
-
-        @Override
-        public void write(int b) throws IOException {
-            size++;
-        }
-
-        @Override
-        public void write(byte[] b) throws IOException {
-            size += b.length;
-        }
-
-        @Override
-        public void write(byte[] b, int off, int len) throws IOException {
-            size += len;
-        }
-    }
-}
\ No newline at end of file
diff --git a/subprojects/core/src/main/groovy/org/gradle/api/internal/file/copy/MatchingCopyAction.java b/subprojects/core/src/main/groovy/org/gradle/api/internal/file/copy/MatchingCopyAction.java
new file mode 100644
index 0000000..74329be
--- /dev/null
+++ b/subprojects/core/src/main/groovy/org/gradle/api/internal/file/copy/MatchingCopyAction.java
@@ -0,0 +1,40 @@
+/*
+ * Copyright 2010 the original author or authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.gradle.api.internal.file.copy;
+
+import org.gradle.api.Action;
+import org.gradle.api.file.FileCopyDetails;
+import org.gradle.api.file.RelativePath;
+import org.gradle.api.specs.Spec;
+
+public class MatchingCopyAction implements Action<FileCopyDetails> {
+
+    private final Spec<RelativePath> matchSpec;
+
+    private final Action<? super FileCopyDetails> toApply;
+
+    public MatchingCopyAction(Spec<RelativePath> matchSpec, Action<? super FileCopyDetails> toApply) {
+        this.matchSpec = matchSpec;
+        this.toApply = toApply;
+    }
+
+    public void execute(FileCopyDetails details) {
+        if (matchSpec.isSatisfiedBy(details.getRelativePath())) {
+            toApply.execute(details);
+        }
+    }
+
+}
diff --git a/subprojects/core/src/main/groovy/org/gradle/api/internal/file/copy/NormalizingCopyActionDecorator.java b/subprojects/core/src/main/groovy/org/gradle/api/internal/file/copy/NormalizingCopyActionDecorator.java
new file mode 100644
index 0000000..ab51d92
--- /dev/null
+++ b/subprojects/core/src/main/groovy/org/gradle/api/internal/file/copy/NormalizingCopyActionDecorator.java
@@ -0,0 +1,189 @@
+/*
+ * 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 com.google.common.collect.ArrayListMultimap;
+import com.google.common.collect.ListMultimap;
+import groovy.lang.Closure;
+import org.gradle.api.file.ContentFilterable;
+import org.gradle.api.file.DuplicatesStrategy;
+import org.gradle.api.file.RelativePath;
+import org.gradle.api.internal.file.AbstractFileTreeElement;
+import org.gradle.api.internal.file.CopyActionProcessingStreamAction;
+import org.gradle.api.tasks.WorkResult;
+
+import java.io.File;
+import java.io.FilterReader;
+import java.io.InputStream;
+import java.util.*;
+
+/**
+ * A {@link CopyAction} 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 NormalizingCopyActionDecorator implements CopyAction {
+
+    private final CopyAction delegate;
+
+    public NormalizingCopyActionDecorator(CopyAction delegate) {
+        this.delegate = delegate;
+    }
+
+    public WorkResult execute(final CopyActionProcessingStream stream) {
+        final Set<RelativePath> visitedDirs = new HashSet<RelativePath>();
+        final ListMultimap<RelativePath, FileCopyDetailsInternal> pendingDirs = ArrayListMultimap.create();
+
+        WorkResult result = delegate.execute(new CopyActionProcessingStream() {
+            public void process(final CopyActionProcessingStreamAction action) {
+
+
+                stream.process(new CopyActionProcessingStreamAction() {
+                    public void processFile(FileCopyDetailsInternal details) {
+                        if (details.isDirectory()) {
+                            RelativePath path = details.getRelativePath();
+                            if (!visitedDirs.contains(path)) {
+                                pendingDirs.put(path, details);
+                            }
+                        } else {
+                            maybeVisit(details.getRelativePath().getParent(), details.isIncludeEmptyDirs(), action);
+                            action.processFile(details);
+                        }
+                    }
+                });
+
+                for (RelativePath path : new LinkedHashSet<RelativePath>(pendingDirs.keySet())) {
+                    List<FileCopyDetailsInternal> detailsList = new ArrayList<FileCopyDetailsInternal>(pendingDirs.get(path));
+                    for (FileCopyDetailsInternal details : detailsList) {
+                        if (details.isIncludeEmptyDirs()) {
+                            maybeVisit(path, details.isIncludeEmptyDirs(), action);
+                        }
+                    }
+                }
+
+                visitedDirs.clear();
+                pendingDirs.clear();
+            }
+
+            private void maybeVisit(RelativePath path, boolean includeEmptyDirs, CopyActionProcessingStreamAction delegateAction) {
+                if (path == null || path.getParent() == null || !visitedDirs.add(path)) {
+                    return;
+                }
+                maybeVisit(path.getParent(), includeEmptyDirs, delegateAction);
+                List<FileCopyDetailsInternal> detailsForPath = pendingDirs.removeAll(path);
+
+                FileCopyDetailsInternal dir;
+                if (detailsForPath.isEmpty()) {
+                    // TODO - this is pretty nasty, look at avoiding using a time bomb stub here
+                    dir = new StubbedFileCopyDetails(path, includeEmptyDirs);
+                } else {
+                    dir = detailsForPath.get(0);
+                }
+                delegateAction.processFile(dir);
+            }
+        });
+
+        return result;
+    }
+
+
+    private static class StubbedFileCopyDetails extends AbstractFileTreeElement implements FileCopyDetailsInternal {
+        private final RelativePath path;
+        private final boolean includeEmptyDirs;
+        private long lastModified = System.currentTimeMillis();
+
+        private StubbedFileCopyDetails(RelativePath path, boolean includeEmptyDirs) {
+            this.path = path;
+            this.includeEmptyDirs = includeEmptyDirs;
+        }
+
+        public boolean isIncludeEmptyDirs() {
+            return includeEmptyDirs;
+        }
+
+        @Override
+        public String getDisplayName() {
+            return path.toString();
+        }
+
+        public File getFile() {
+            throw new UnsupportedOperationException();
+        }
+
+        public boolean isDirectory() {
+            return !path.isFile();
+        }
+
+        public long getLastModified() {
+            return lastModified;
+        }
+
+        public long getSize() {
+            throw new UnsupportedOperationException();
+        }
+
+        public InputStream open() {
+            throw new UnsupportedOperationException();
+        }
+
+        public RelativePath getRelativePath() {
+            return path;
+        }
+
+        public void exclude() {
+            throw new UnsupportedOperationException();
+        }
+
+        public void setName(String name) {
+            throw new UnsupportedOperationException();
+        }
+
+        public void setPath(String path) {
+            throw new UnsupportedOperationException();
+        }
+
+        public void setRelativePath(RelativePath path) {
+            throw new UnsupportedOperationException();
+        }
+
+        public void setMode(int mode) {
+            throw new UnsupportedOperationException();
+        }
+
+        public void setDuplicatesStrategy(DuplicatesStrategy strategy) {
+            throw new UnsupportedOperationException();
+        }
+
+        public DuplicatesStrategy getDuplicatesStrategy() {
+            throw new UnsupportedOperationException();
+        }
+
+        public ContentFilterable filter(Map<String, ?> properties, Class<? extends FilterReader> filterType) {
+            throw new UnsupportedOperationException();
+        }
+
+        public ContentFilterable filter(Class<? extends FilterReader> filterType) {
+            throw new UnsupportedOperationException();
+        }
+
+        public ContentFilterable filter(Closure closure) {
+            throw new UnsupportedOperationException();
+        }
+
+        public ContentFilterable expand(Map<String, ?> properties) {
+            throw new UnsupportedOperationException();
+        }
+    }
+}
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
deleted file mode 100644
index f35c3e6..0000000
--- a/subprojects/core/src/main/groovy/org/gradle/api/internal/file/copy/NormalizingCopySpecVisitor.java
+++ /dev/null
@@ -1,121 +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.copy;
-
-import org.gradle.api.file.FileVisitDetails;
-import org.gradle.api.file.RelativePath;
-import org.gradle.api.internal.file.AbstractFileTreeElement;
-
-import java.io.File;
-import java.io.InputStream;
-import java.util.*;
-
-/**
- * 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>();
-
-    public NormalizingCopySpecVisitor(CopySpecVisitor visitor) {
-        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();
-    }
-
-    private void maybeVisit(RelativePath path) {
-        if (path == null || path.getParent() == null || !visitedDirs.add(path)) {
-            return;
-        }
-        maybeVisit(path.getParent());
-        FileVisitDetails dir = pendingDirs.remove(path);
-        if (dir == null) {
-            dir = new FileVisitDetailsImpl(path);
-        }
-        getVisitor().visitDir(dir);
-    }
-
-    public void visitFile(FileVisitDetails fileDetails) {
-        maybeVisit(fileDetails.getRelativePath().getParent());
-        getVisitor().visitFile(fileDetails);
-    }
-
-    public void visitDir(FileVisitDetails dirDetails) {
-        RelativePath path = dirDetails.getRelativePath();
-        if (!visitedDirs.contains(path)) {
-            pendingDirs.put(path, dirDetails);
-        }
-    }
-
-    private static class FileVisitDetailsImpl extends AbstractFileTreeElement implements FileVisitDetails {
-        private final RelativePath path;
-        private long lastModified = System.currentTimeMillis();
-
-        private FileVisitDetailsImpl(RelativePath path) {
-            this.path = path;
-        }
-
-        @Override
-        public String getDisplayName() {
-            return path.toString();
-        }
-
-        public void stopVisiting() {
-            throw new UnsupportedOperationException();
-        }
-
-        public File getFile() {
-            throw new UnsupportedOperationException();
-        }
-
-        public boolean isDirectory() {
-            return !path.isFile();
-        }
-
-        public long getLastModified() {
-            return lastModified;
-        }
-
-        public long getSize() {
-            throw new UnsupportedOperationException();
-        }
-
-        public InputStream open() {
-            throw new UnsupportedOperationException();
-        }
-
-        public RelativePath getRelativePath() {
-            return path;
-        }
-    }
-}
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 6a623b8..82334d8 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
@@ -1,5 +1,5 @@
 /*
- * Copyright 2009 the original author or authors.
+ * Copyright 2013 the original author or authors.
  *
  * Licensed under the Apache License, Version 2.0 (the "License");
  * you may not use this file except in compliance with the License.
@@ -13,29 +13,15 @@
  * 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.Action;
-import org.gradle.api.file.FileCopyDetails;
-import org.gradle.api.file.FileTree;
-import org.gradle.api.file.RelativePath;
 
-import java.util.Collection;
+package org.gradle.api.internal.file.copy;
 
+/**
+ * DO NOT REMOVE.
+ *
+ * Prior to 1.8, Copy implemented this so it is needed to keep backwards compatibility.
+ */
+ at SuppressWarnings("UnusedDeclaration")
+ at Deprecated
 public interface ReadableCopySpec {
-    RelativePath getDestPath();
-
-    Integer getFileMode();
-
-    Integer getDirMode();
-
-    FileTree getSource();
-
-    Collection<? extends ReadableCopySpec> getAllSpecs();
-
-    boolean hasSource();
-
-    Collection<? extends Action<? super FileCopyDetails>> getAllCopyActions();
-
-    boolean getIncludeEmptyDirs();
 }
diff --git a/subprojects/core/src/main/groovy/org/gradle/api/internal/file/copy/RegExpNameMapper.java b/subprojects/core/src/main/groovy/org/gradle/api/internal/file/copy/RegExpNameMapper.java
index c871185..55df249 100644
--- a/subprojects/core/src/main/groovy/org/gradle/api/internal/file/copy/RegExpNameMapper.java
+++ b/subprojects/core/src/main/groovy/org/gradle/api/internal/file/copy/RegExpNameMapper.java
@@ -20,9 +20,6 @@ import org.gradle.api.Transformer;
 import java.util.regex.Matcher;
 import java.util.regex.Pattern;
 
-/**
- * @author Steve Appling
- */
 public class RegExpNameMapper implements Transformer<String, String> {
     private Matcher matcher;
     private String replacement;
diff --git a/subprojects/core/src/main/groovy/org/gradle/api/internal/file/copy/RelativizedCopySpec.java b/subprojects/core/src/main/groovy/org/gradle/api/internal/file/copy/RelativizedCopySpec.java
new file mode 100644
index 0000000..5a945bf
--- /dev/null
+++ b/subprojects/core/src/main/groovy/org/gradle/api/internal/file/copy/RelativizedCopySpec.java
@@ -0,0 +1,51 @@
+/*
+ * Copyright 2013 the original author or authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY 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.Transformer;
+import org.gradle.api.file.RelativePath;
+
+import static org.gradle.util.CollectionUtils.collect;
+
+public class RelativizedCopySpec extends DelegatingCopySpec {
+
+    private final CopySpecInternal parent;
+    private final CopySpecInternal child;
+
+    public RelativizedCopySpec(CopySpecInternal parent, CopySpecInternal child) {
+        this.parent = parent;
+        this.child = child;
+    }
+
+    @Override
+    protected CopySpecInternal getDelegateCopySpec() {
+        return child;
+    }
+
+    public RelativePath getDestPath() {
+        return parent.getDestPath().append(child.getDestPath());
+    }
+
+    @Override
+    public Iterable<CopySpecInternal> getChildren() {
+        return collect(super.getChildren(), new Transformer<CopySpecInternal, CopySpecInternal>() {
+            public CopySpecInternal transform(CopySpecInternal original) {
+                return new RelativizedCopySpec(parent, original);
+            }
+        });
+    }
+}
diff --git a/subprojects/core/src/main/groovy/org/gradle/api/internal/file/copy/SyncCopyActionDecorator.java b/subprojects/core/src/main/groovy/org/gradle/api/internal/file/copy/SyncCopyActionDecorator.java
new file mode 100644
index 0000000..68a12b1
--- /dev/null
+++ b/subprojects/core/src/main/groovy/org/gradle/api/internal/file/copy/SyncCopyActionDecorator.java
@@ -0,0 +1,92 @@
+/*
+ * Copyright 2009 the original author or authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.gradle.api.internal.file.copy;
+
+import org.gradle.api.file.FileVisitDetails;
+import org.gradle.api.file.FileVisitor;
+import org.gradle.api.file.RelativePath;
+import org.gradle.api.internal.file.CopyActionProcessingStreamAction;
+import org.gradle.api.internal.file.collections.DirectoryFileTree;
+import org.gradle.api.internal.file.collections.MinimalFileTree;
+import org.gradle.api.internal.tasks.SimpleWorkResult;
+import org.gradle.api.tasks.WorkResult;
+import org.gradle.util.GFileUtils;
+
+import java.io.File;
+import java.util.HashSet;
+import java.util.Set;
+
+public class SyncCopyActionDecorator implements CopyAction {
+    private final File baseDestDir;
+    private final CopyAction delegate;
+
+    public SyncCopyActionDecorator(File baseDestDir, CopyAction delegate) {
+        this.baseDestDir = baseDestDir;
+        this.delegate = delegate;
+    }
+
+    public WorkResult execute(final CopyActionProcessingStream stream) {
+        final Set<RelativePath> visited = new HashSet<RelativePath>();
+
+        WorkResult didWork = delegate.execute(new CopyActionProcessingStream() {
+            public void process(final CopyActionProcessingStreamAction action) {
+                stream.process(new CopyActionProcessingStreamAction() {
+                    public void processFile(FileCopyDetailsInternal details) {
+                        visited.add(details.getRelativePath());
+                        action.processFile(details);
+                    }
+                });
+            }
+        });
+
+        SyncCopyActionDecoratorFileVisitor fileVisitor = new SyncCopyActionDecoratorFileVisitor(visited);
+
+        MinimalFileTree walker = new DirectoryFileTree(baseDestDir).postfix();
+        walker.visit(fileVisitor);
+        visited.clear();
+
+        return new SimpleWorkResult(didWork.getDidWork() || fileVisitor.didWork);
+    }
+
+    private static class SyncCopyActionDecoratorFileVisitor implements FileVisitor {
+        private final Set<RelativePath> visited;
+        private boolean didWork;
+
+        private SyncCopyActionDecoratorFileVisitor(Set<RelativePath> visited) {
+            this.visited = visited;
+        }
+
+        public void visitDir(FileVisitDetails dirDetails) {
+            maybeDelete(dirDetails, true);
+        }
+
+        public void visitFile(FileVisitDetails fileDetails) {
+            maybeDelete(fileDetails, false);
+        }
+
+        private void maybeDelete(FileVisitDetails fileDetails, boolean isDir) {
+            RelativePath path = fileDetails.getRelativePath();
+            if (!visited.contains(path)) {
+                if (isDir) {
+                    GFileUtils.deleteDirectory(fileDetails.getFile());
+                } else {
+                    GFileUtils.deleteQuietly(fileDetails.getFile());
+                }
+                didWork = true;
+            }
+        }
+    }
+}
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
deleted file mode 100644
index 74783f3..0000000
--- a/subprojects/core/src/main/groovy/org/gradle/api/internal/file/copy/SyncCopySpecVisitor.java
+++ /dev/null
@@ -1,90 +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.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.collections.DirectoryFileTree;
-import org.gradle.api.internal.file.collections.MinimalFileTree;
-import org.gradle.util.GFileUtils;
-
-import java.io.File;
-import java.util.HashSet;
-import java.util.Set;
-
-public class SyncCopySpecVisitor extends DelegatingCopySpecVisitor {
-    private final Set<RelativePath> visited = new HashSet<RelativePath>();
-    private File baseDestDir;
-    private boolean didWork;
-
-    public SyncCopySpecVisitor(CopySpecVisitor visitor) {
-        super(visitor);
-    }
-
-    public void startVisit(CopyAction action) {
-        baseDestDir = ((FileCopyAction) action).getDestinationDir();
-        getVisitor().startVisit(action);
-    }
-
-    @Override
-    public void visitDir(FileVisitDetails dirDetails) {
-        visited.add(dirDetails.getRelativePath());
-        getVisitor().visitDir(dirDetails);
-    }
-
-    @Override
-    public void visitFile(FileVisitDetails fileDetails) {
-        visited.add(fileDetails.getRelativePath());
-        getVisitor().visitFile(fileDetails);
-    }
-
-    @Override
-    public void endVisit() {
-        FileVisitor visitor = new FileVisitor() {
-            public void visitDir(FileVisitDetails dirDetails) {
-                maybeDelete(dirDetails, true);
-            }
-
-            public void visitFile(FileVisitDetails fileDetails) {
-                maybeDelete(fileDetails, false);
-            }
-
-            private void maybeDelete(FileVisitDetails fileDetails, boolean isDir) {
-                RelativePath path = fileDetails.getRelativePath();
-                if (!visited.contains(path)) {
-                    if (isDir) {
-                        GFileUtils.deleteDirectory(fileDetails.getFile());
-                    } else {
-                        GFileUtils.deleteQuietly(fileDetails.getFile());
-                    }
-                    didWork = true;
-                }
-            }
-        };
-
-        MinimalFileTree walker = new DirectoryFileTree(baseDestDir).postfix();
-        walker.visit(visitor);
-        visited.clear();
-
-        getVisitor().endVisit();
-    }
-
-    @Override
-    public boolean getDidWork() {
-        return didWork || getVisitor().getDidWork();
-    }
-}
diff --git a/subprojects/core/src/main/groovy/org/gradle/api/internal/file/pattern/DefaultPatternMatcher.java b/subprojects/core/src/main/groovy/org/gradle/api/internal/file/pattern/DefaultPatternMatcher.java
index 53dd653..3835636 100644
--- a/subprojects/core/src/main/groovy/org/gradle/api/internal/file/pattern/DefaultPatternMatcher.java
+++ b/subprojects/core/src/main/groovy/org/gradle/api/internal/file/pattern/DefaultPatternMatcher.java
@@ -18,13 +18,10 @@ package org.gradle.api.internal.file.pattern;
 import org.gradle.api.file.RelativePath;
 import org.gradle.api.specs.Spec;
 
-import java.util.List;
 import java.util.ArrayList;
+import java.util.List;
 import java.util.ListIterator;
 
-/**
- * @author Steve Appling
- */
 public class DefaultPatternMatcher implements Spec<RelativePath> {
     private List<PatternStep> steps;
     private boolean partialMatchDirs;
diff --git a/subprojects/core/src/main/groovy/org/gradle/api/internal/file/pattern/GreedyPatternStep.java b/subprojects/core/src/main/groovy/org/gradle/api/internal/file/pattern/GreedyPatternStep.java
index 7903443..e3a162d 100644
--- a/subprojects/core/src/main/groovy/org/gradle/api/internal/file/pattern/GreedyPatternStep.java
+++ b/subprojects/core/src/main/groovy/org/gradle/api/internal/file/pattern/GreedyPatternStep.java
@@ -15,9 +15,6 @@
  */
 package org.gradle.api.internal.file.pattern;
 
-/**
- * @author Steve Appling
- */
 public class GreedyPatternStep implements PatternStep{
     public boolean matches(String candidate, boolean isFile) {
         return true;
diff --git a/subprojects/core/src/main/groovy/org/gradle/api/internal/file/pattern/NameOnlyPatternMatcher.java b/subprojects/core/src/main/groovy/org/gradle/api/internal/file/pattern/NameOnlyPatternMatcher.java
index 5092754..f29ba81 100644
--- a/subprojects/core/src/main/groovy/org/gradle/api/internal/file/pattern/NameOnlyPatternMatcher.java
+++ b/subprojects/core/src/main/groovy/org/gradle/api/internal/file/pattern/NameOnlyPatternMatcher.java
@@ -24,7 +24,6 @@ import org.gradle.api.specs.Spec;
  *
  * This will only match against the last part of a relative path and this only if
  * the RelativePath is a File.
- * @author Steve Appling
  */
 public class NameOnlyPatternMatcher implements Spec<RelativePath> {
     private boolean partialMatchDirs;
diff --git a/subprojects/core/src/main/groovy/org/gradle/api/internal/file/pattern/PatternMatcherFactory.java b/subprojects/core/src/main/groovy/org/gradle/api/internal/file/pattern/PatternMatcherFactory.java
index 9a8670f..cced12b 100644
--- a/subprojects/core/src/main/groovy/org/gradle/api/internal/file/pattern/PatternMatcherFactory.java
+++ b/subprojects/core/src/main/groovy/org/gradle/api/internal/file/pattern/PatternMatcherFactory.java
@@ -18,9 +18,6 @@ package org.gradle.api.internal.file.pattern;
 import org.gradle.api.file.RelativePath;
 import org.gradle.api.specs.Spec;
 
-/**
- * @author Steve Appling
- */
 public class PatternMatcherFactory {
     public static Spec<RelativePath> getPatternMatcher(boolean partialMatchDirs, boolean caseSensitive, String pattern) {
         // trailing / or \ assumes **
diff --git a/subprojects/core/src/main/groovy/org/gradle/api/internal/file/pattern/PatternStep.java b/subprojects/core/src/main/groovy/org/gradle/api/internal/file/pattern/PatternStep.java
index ddc46af..938eca4 100644
--- a/subprojects/core/src/main/groovy/org/gradle/api/internal/file/pattern/PatternStep.java
+++ b/subprojects/core/src/main/groovy/org/gradle/api/internal/file/pattern/PatternStep.java
@@ -15,9 +15,6 @@
  */
 package org.gradle.api.internal.file.pattern;
 
-/**
- * @author Steve Appling
- */
 public interface PatternStep {
     public boolean matches(String candidate, boolean isFile);
     public boolean isGreedy();
diff --git a/subprojects/core/src/main/groovy/org/gradle/api/internal/file/pattern/RegExpPatternStep.java b/subprojects/core/src/main/groovy/org/gradle/api/internal/file/pattern/RegExpPatternStep.java
index ae80553..39dc6c8 100644
--- a/subprojects/core/src/main/groovy/org/gradle/api/internal/file/pattern/RegExpPatternStep.java
+++ b/subprojects/core/src/main/groovy/org/gradle/api/internal/file/pattern/RegExpPatternStep.java
@@ -18,9 +18,6 @@ package org.gradle.api.internal.file.pattern;
 import java.util.regex.Matcher;
 import java.util.regex.Pattern;
 
-/**
- * @author Steve Appling
- */
 public class RegExpPatternStep implements PatternStep {
     private static final String ESCAPE_CHARS = "\\[]^-&.{}()$+|<=!";
     private static final String PATTERN_CHARS = "*?";
diff --git a/subprojects/core/src/main/groovy/org/gradle/api/internal/filestore/AbstractFileStoreEntry.java b/subprojects/core/src/main/groovy/org/gradle/api/internal/filestore/AbstractFileStoreEntry.java
deleted file mode 100644
index 00bf480..0000000
--- a/subprojects/core/src/main/groovy/org/gradle/api/internal/filestore/AbstractFileStoreEntry.java
+++ /dev/null
@@ -1,28 +0,0 @@
-/*
- * Copyright 2012 the original author or authors.
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- *      http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package org.gradle.api.internal.filestore;
-
-import org.gradle.util.hash.HashUtil;
-import org.gradle.util.hash.HashValue;
-
-public abstract class AbstractFileStoreEntry implements FileStoreEntry {
-
-    public HashValue getSha1() {
-        return HashUtil.createHash(getFile(), "SHA1");
-    }
-
-}
diff --git a/subprojects/core/src/main/groovy/org/gradle/api/internal/filestore/FileStore.java b/subprojects/core/src/main/groovy/org/gradle/api/internal/filestore/FileStore.java
deleted file mode 100644
index bdf2339..0000000
--- a/subprojects/core/src/main/groovy/org/gradle/api/internal/filestore/FileStore.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.api.internal.filestore;
-
-import org.gradle.api.Action;
-
-import java.io.File;
-
-public interface FileStore<K> {
-
-    FileStoreEntry move(K key, File source);
-
-    FileStoreEntry copy(K key, File source);
-
-    void moveFilestore(File destination);
-
-    FileStoreEntry add(K key, Action<File> addAction);
-}
diff --git a/subprojects/core/src/main/groovy/org/gradle/api/internal/filestore/FileStoreEntry.java b/subprojects/core/src/main/groovy/org/gradle/api/internal/filestore/FileStoreEntry.java
deleted file mode 100644
index 76298a5..0000000
--- a/subprojects/core/src/main/groovy/org/gradle/api/internal/filestore/FileStoreEntry.java
+++ /dev/null
@@ -1,29 +0,0 @@
-/*
- * Copyright 2012 the original author or authors.
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- *      http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package org.gradle.api.internal.filestore;
-
-import org.gradle.util.hash.HashValue;
-
-import java.io.File;
-
-public interface FileStoreEntry {
-
-    File getFile();
-
-    HashValue getSha1();
-
-}
diff --git a/subprojects/core/src/main/groovy/org/gradle/api/internal/filestore/FileStoreSearcher.java b/subprojects/core/src/main/groovy/org/gradle/api/internal/filestore/FileStoreSearcher.java
deleted file mode 100644
index b23bdfd..0000000
--- a/subprojects/core/src/main/groovy/org/gradle/api/internal/filestore/FileStoreSearcher.java
+++ /dev/null
@@ -1,25 +0,0 @@
-/*
- * Copyright 2012 the original author or authors.
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- *      http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package org.gradle.api.internal.filestore;
-
-import java.util.Set;
-
-public interface FileStoreSearcher<S> {
-
-    Set<? extends FileStoreEntry> search(S key);
-
-}
diff --git a/subprojects/core/src/main/groovy/org/gradle/api/internal/filestore/GroupedAndNamedUniqueFileStore.java b/subprojects/core/src/main/groovy/org/gradle/api/internal/filestore/GroupedAndNamedUniqueFileStore.java
index 8127a95..ef9d545 100644
--- a/subprojects/core/src/main/groovy/org/gradle/api/internal/filestore/GroupedAndNamedUniqueFileStore.java
+++ b/subprojects/core/src/main/groovy/org/gradle/api/internal/filestore/GroupedAndNamedUniqueFileStore.java
@@ -18,6 +18,9 @@ package org.gradle.api.internal.filestore;
 import org.gradle.api.Action;
 import org.gradle.api.Transformer;
 import org.gradle.api.internal.file.TemporaryFileProvider;
+import org.gradle.internal.filestore.FileStore;
+import org.gradle.internal.filestore.FileStoreSearcher;
+import org.gradle.internal.resource.local.LocallyAvailableResource;
 import org.gradle.util.hash.HashUtil;
 
 import java.io.File;
@@ -38,15 +41,15 @@ public class GroupedAndNamedUniqueFileStore<K> implements FileStore<K>, FileStor
         this.namer = namer;
     }
 
-    public FileStoreEntry move(K key, File source) {
+    public LocallyAvailableResource move(K key, File source) {
         return delegate.move(toPath(key, getChecksum(source)), source);
     }
 
-    public FileStoreEntry copy(K key, File source) {
+    public LocallyAvailableResource copy(K key, File source) {
         return delegate.copy(toPath(key, getChecksum(source)), source);
     }
 
-    public Set<? extends FileStoreEntry> search(K key) {
+    public Set<? extends LocallyAvailableResource> search(K key) {
         return delegate.search(toPath(key, "*"));
     }
 
@@ -69,7 +72,7 @@ public class GroupedAndNamedUniqueFileStore<K> implements FileStore<K>, FileStor
         delegate.moveFilestore(destination);
     }
 
-    public FileStoreEntry add(K key, Action<File> addAction) {
+    public LocallyAvailableResource add(K key, Action<File> addAction) {
         //We cannot just delegate to the add method as we need the file content for checksum calculation here
         //and reexecuting the action isn't acceptable
         final File tempFile = getTempFile();
diff --git a/subprojects/core/src/main/groovy/org/gradle/api/internal/filestore/PathKeyFileStore.java b/subprojects/core/src/main/groovy/org/gradle/api/internal/filestore/PathKeyFileStore.java
index e8f0be9..e5b8556 100644
--- a/subprojects/core/src/main/groovy/org/gradle/api/internal/filestore/PathKeyFileStore.java
+++ b/subprojects/core/src/main/groovy/org/gradle/api/internal/filestore/PathKeyFileStore.java
@@ -24,6 +24,10 @@ import org.gradle.api.internal.file.IdentityFileResolver;
 import org.gradle.api.internal.file.collections.MinimalFileTree;
 import org.gradle.api.internal.file.collections.SingleIncludePatternFileTree;
 import org.gradle.api.internal.file.copy.DeleteActionImpl;
+import org.gradle.internal.filestore.FileStore;
+import org.gradle.internal.filestore.FileStoreSearcher;
+import org.gradle.internal.resource.local.AbstractLocallyAvailableResource;
+import org.gradle.internal.resource.local.LocallyAvailableResource;
 import org.gradle.util.GFileUtils;
 
 import java.io.File;
@@ -64,11 +68,11 @@ public class PathKeyFileStore implements FileStore<String>, FileStoreSearcher<St
         return baseDir;
     }
 
-    public FileStoreEntry move(String path, File source) {
+    public LocallyAvailableResource move(String path, File source) {
         return saveIntoFileStore(source, getFile(path), true);
     }
 
-    public FileStoreEntry copy(String path, File source) {
+    public LocallyAvailableResource copy(String path, File source) {
         return saveIntoFileStore(source, getFile(path), false);
     }
 
@@ -93,12 +97,12 @@ public class PathKeyFileStore implements FileStore<String>, FileStoreSearcher<St
         baseDir = destination;
     }
 
-    public FileStoreEntry add(String path, Action<File> addAction) {
+    public LocallyAvailableResource add(String path, Action<File> addAction) {
         String error = String.format("Failed to add into filestore '%s' at '%s' ", getBaseDir().getAbsolutePath(), path);
         return doAdd(getFile(path), error, addAction);
     }
 
-    protected FileStoreEntry saveIntoFileStore(final File source, final File destination, final boolean isMove) {
+    protected LocallyAvailableResource saveIntoFileStore(final File source, final File destination, final boolean isMove) {
         String verb = isMove ? "move" : "copy";
 
         if (!source.exists()) {
@@ -118,7 +122,7 @@ public class PathKeyFileStore implements FileStore<String>, FileStoreSearcher<St
         });
     }
 
-    protected FileStoreEntry doAdd(File destination, String failureDescription, Action<File> action) {
+    protected LocallyAvailableResource doAdd(File destination, String failureDescription, Action<File> action) {
         try {
             GFileUtils.parentMkdirs(destination);
             File inProgressMarkerFile = getInProgressMarkerFile(destination);
@@ -138,12 +142,12 @@ public class PathKeyFileStore implements FileStore<String>, FileStoreSearcher<St
         return entryAt(destination);
     }
 
-    public Set<? extends FileStoreEntry> search(String pattern) {
+    public Set<? extends LocallyAvailableResource> search(String pattern) {
         if (!getBaseDir().exists()) {
             return Collections.emptySet();
         }
 
-        final Set<FileStoreEntry> entries = new HashSet<FileStoreEntry>();
+        final Set<LocallyAvailableResource> entries = new HashSet<LocallyAvailableResource>();
         findFiles(pattern).visit(new EmptyFileVisitor() {
             public void visitFile(FileVisitDetails fileDetails) {
                 final File file = fileDetails.getFile();
@@ -174,19 +178,19 @@ public class PathKeyFileStore implements FileStore<String>, FileStoreSearcher<St
         return new SingleIncludePatternFileTree(baseDir, pattern);
     }
 
-    protected FileStoreEntry entryAt(File file) {
+    protected LocallyAvailableResource entryAt(File file) {
         return entryAt(GFileUtils.relativePath(baseDir, file));
     }
 
-    protected FileStoreEntry entryAt(final String path) {
-        return new AbstractFileStoreEntry() {
+    protected LocallyAvailableResource entryAt(final String path) {
+        return new AbstractLocallyAvailableResource() {
             public File getFile() {
                 return new File(baseDir, path);
             }
         };
     }
 
-    public FileStoreEntry get(String key) {
+    public LocallyAvailableResource get(String key) {
         final File file = getFileWhileCleaningInProgress(key);
         if (file.exists()) {
             return entryAt(file);
diff --git a/subprojects/core/src/main/groovy/org/gradle/api/internal/filestore/PathNormalisingKeyFileStore.java b/subprojects/core/src/main/groovy/org/gradle/api/internal/filestore/PathNormalisingKeyFileStore.java
index ab03a5a..be47d6e 100644
--- a/subprojects/core/src/main/groovy/org/gradle/api/internal/filestore/PathNormalisingKeyFileStore.java
+++ b/subprojects/core/src/main/groovy/org/gradle/api/internal/filestore/PathNormalisingKeyFileStore.java
@@ -17,6 +17,9 @@
 package org.gradle.api.internal.filestore;
 
 import org.gradle.api.Action;
+import org.gradle.internal.filestore.FileStore;
+import org.gradle.internal.filestore.FileStoreSearcher;
+import org.gradle.internal.resource.local.LocallyAvailableResource;
 
 import java.io.File;
 import java.util.Set;
@@ -33,11 +36,11 @@ public class PathNormalisingKeyFileStore implements FileStore<String>, FileStore
         this.delegate = delegate;
     }
 
-    public FileStoreEntry move(String key, File source) {
+    public LocallyAvailableResource move(String key, File source) {
         return delegate.move(normalizePath(key), source);
     }
 
-    public FileStoreEntry copy(String key, File source) {
+    public LocallyAvailableResource copy(String key, File source) {
         return delegate.copy(key, source);
     }
 
@@ -53,11 +56,11 @@ public class PathNormalisingKeyFileStore implements FileStore<String>, FileStore
         delegate.moveFilestore(destination);
     }
 
-    public FileStoreEntry add(String key, Action<File> addAction) {
+    public LocallyAvailableResource add(String key, Action<File> addAction) {
         return delegate.add(normalizePath(key), addAction);
     }
 
-    public Set<? extends FileStoreEntry> search(String key) {
+    public Set<? extends LocallyAvailableResource> search(String key) {
         return delegate.search(normalizeSearchPath(key));
     }
 }
diff --git a/subprojects/core/src/main/groovy/org/gradle/api/internal/filestore/UniquePathKeyFileStore.java b/subprojects/core/src/main/groovy/org/gradle/api/internal/filestore/UniquePathKeyFileStore.java
index 21c759c..4ba34b0 100644
--- a/subprojects/core/src/main/groovy/org/gradle/api/internal/filestore/UniquePathKeyFileStore.java
+++ b/subprojects/core/src/main/groovy/org/gradle/api/internal/filestore/UniquePathKeyFileStore.java
@@ -17,6 +17,7 @@
 package org.gradle.api.internal.filestore;
 
 import org.gradle.api.Action;
+import org.gradle.internal.resource.local.LocallyAvailableResource;
 import org.gradle.util.GFileUtils;
 
 import java.io.File;
@@ -33,8 +34,8 @@ public class UniquePathKeyFileStore extends PathKeyFileStore {
     }
 
     @Override
-    public FileStoreEntry move(String path, File source) {
-        FileStoreEntry entry = super.move(path, source);
+    public LocallyAvailableResource move(String path, File source) {
+        LocallyAvailableResource entry = super.move(path, source);
         if (source.exists()) {
             GFileUtils.deleteQuietly(source);
         }
@@ -42,7 +43,7 @@ public class UniquePathKeyFileStore extends PathKeyFileStore {
     }
 
     @Override
-    protected FileStoreEntry doAdd(File destination, String failureDescription, Action<File> action) {
+    protected LocallyAvailableResource doAdd(File destination, String failureDescription, Action<File> action) {
         if (destination.exists()) {
             return entryAt(destination);
         }
diff --git a/subprojects/core/src/main/groovy/org/gradle/api/internal/initialization/AbstractScriptHandler.java b/subprojects/core/src/main/groovy/org/gradle/api/internal/initialization/AbstractScriptHandler.java
index 27c19f6..8595bcd 100644
--- a/subprojects/core/src/main/groovy/org/gradle/api/internal/initialization/AbstractScriptHandler.java
+++ b/subprojects/core/src/main/groovy/org/gradle/api/internal/initialization/AbstractScriptHandler.java
@@ -22,7 +22,7 @@ import org.gradle.api.artifacts.dsl.DependencyHandler;
 import org.gradle.api.artifacts.dsl.RepositoryHandler;
 import org.gradle.groovy.scripts.ScriptSource;
 import org.gradle.util.ConfigureUtil;
-import org.gradle.util.MutableURLClassLoader;
+import org.gradle.internal.classloader.MutableURLClassLoader;
 
 import java.io.File;
 import java.net.URI;
@@ -43,7 +43,7 @@ public abstract class AbstractScriptHandler implements ScriptHandlerInternal {
         this.dependencyHandler = dependencyHandler;
         this.scriptSource = scriptSource;
         this.configContainer = configContainer;
-        classpathConfiguration = configContainer.add(CLASSPATH_CONFIGURATION);
+        classpathConfiguration = configContainer.create(CLASSPATH_CONFIGURATION);
     }
 
     public void dependencies(Closure configureClosure) {
diff --git a/subprojects/core/src/main/groovy/org/gradle/api/internal/initialization/DefaultScriptHandler.java b/subprojects/core/src/main/groovy/org/gradle/api/internal/initialization/DefaultScriptHandler.java
index a801482..ea95c72 100644
--- a/subprojects/core/src/main/groovy/org/gradle/api/internal/initialization/DefaultScriptHandler.java
+++ b/subprojects/core/src/main/groovy/org/gradle/api/internal/initialization/DefaultScriptHandler.java
@@ -19,7 +19,7 @@ import org.gradle.api.artifacts.ConfigurationContainer;
 import org.gradle.api.artifacts.dsl.DependencyHandler;
 import org.gradle.api.artifacts.dsl.RepositoryHandler;
 import org.gradle.groovy.scripts.ScriptSource;
-import org.gradle.util.MutableURLClassLoader;
+import org.gradle.internal.classloader.MutableURLClassLoader;
 
 import java.io.File;
 import java.net.MalformedURLException;
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 da94f98..aa18738 100755
--- 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
@@ -28,7 +28,7 @@ import org.gradle.api.internal.artifacts.dsl.dependencies.ProjectFinder;
 import org.gradle.api.internal.file.FileResolver;
 import org.gradle.api.internal.project.ProjectInternal;
 import org.gradle.groovy.scripts.ScriptSource;
-import org.gradle.util.MutableURLClassLoader;
+import org.gradle.internal.classloader.MutableURLClassLoader;
 
 import java.util.Arrays;
 import java.util.Collection;
diff --git a/subprojects/core/src/main/groovy/org/gradle/api/internal/initialization/NoClassLoaderUpdateScriptHandler.java b/subprojects/core/src/main/groovy/org/gradle/api/internal/initialization/NoClassLoaderUpdateScriptHandler.java
index 5a596f6..5eb2060 100644
--- a/subprojects/core/src/main/groovy/org/gradle/api/internal/initialization/NoClassLoaderUpdateScriptHandler.java
+++ b/subprojects/core/src/main/groovy/org/gradle/api/internal/initialization/NoClassLoaderUpdateScriptHandler.java
@@ -19,7 +19,7 @@ import org.gradle.api.artifacts.ConfigurationContainer;
 import org.gradle.api.artifacts.dsl.DependencyHandler;
 import org.gradle.api.artifacts.dsl.RepositoryHandler;
 import org.gradle.groovy.scripts.ScriptSource;
-import org.gradle.util.MutableURLClassLoader;
+import org.gradle.internal.classloader.MutableURLClassLoader;
 
 public class NoClassLoaderUpdateScriptHandler extends AbstractScriptHandler {
     public NoClassLoaderUpdateScriptHandler(MutableURLClassLoader classLoader, RepositoryHandler repositoryHandler,
diff --git a/subprojects/core/src/main/groovy/org/gradle/api/internal/notations/NotationParserBuilder.java b/subprojects/core/src/main/groovy/org/gradle/api/internal/notations/NotationParserBuilder.java
index 94906af..53c70b3 100644
--- a/subprojects/core/src/main/groovy/org/gradle/api/internal/notations/NotationParserBuilder.java
+++ b/subprojects/core/src/main/groovy/org/gradle/api/internal/notations/NotationParserBuilder.java
@@ -28,9 +28,6 @@ import java.util.LinkedList;
 import java.util.List;
 import java.util.Set;
 
-/**
- * by Szczepan Faber, created at: 11/8/11
- */
 public class NotationParserBuilder<T> {
     private TypeInfo<T> resultingType;
     private String invalidNotationMessage;
diff --git a/subprojects/core/src/main/groovy/org/gradle/api/internal/notations/TypeInfo.java b/subprojects/core/src/main/groovy/org/gradle/api/internal/notations/TypeInfo.java
index 89993b5..4ccc491 100644
--- a/subprojects/core/src/main/groovy/org/gradle/api/internal/notations/TypeInfo.java
+++ b/subprojects/core/src/main/groovy/org/gradle/api/internal/notations/TypeInfo.java
@@ -18,8 +18,6 @@ package org.gradle.api.internal.notations;
 
 /**
  * Type literal, useful for nested Generics.
- *
- * by Szczepan Faber, created at: 10/12/12
  */
 public class TypeInfo<T> {
     private final Class<T> targetType;
@@ -29,7 +27,7 @@ public class TypeInfo<T> {
         this.targetType = targetType;
     }
 
-    Class<T> getTargetType() {
+    public Class<T> getTargetType() {
         return targetType;
     }
 }
diff --git a/subprojects/core/src/main/groovy/org/gradle/api/internal/notations/api/NotationParser.java b/subprojects/core/src/main/groovy/org/gradle/api/internal/notations/api/NotationParser.java
index e93ab7a..5ab149c 100644
--- a/subprojects/core/src/main/groovy/org/gradle/api/internal/notations/api/NotationParser.java
+++ b/subprojects/core/src/main/groovy/org/gradle/api/internal/notations/api/NotationParser.java
@@ -18,9 +18,6 @@ package org.gradle.api.internal.notations.api;
 
 import java.util.Collection;
 
-/**
- * by Szczepan Faber, created at: 11/8/11
- */
 public interface NotationParser<T> {
     T parseNotation(Object notation) throws UnsupportedNotationException;
 
diff --git a/subprojects/core/src/main/groovy/org/gradle/api/internal/notations/parsers/ClosureToSpecNotationParser.java b/subprojects/core/src/main/groovy/org/gradle/api/internal/notations/parsers/ClosureToSpecNotationParser.java
index 6f9cefd..2d43edb 100644
--- a/subprojects/core/src/main/groovy/org/gradle/api/internal/notations/parsers/ClosureToSpecNotationParser.java
+++ b/subprojects/core/src/main/groovy/org/gradle/api/internal/notations/parsers/ClosureToSpecNotationParser.java
@@ -24,9 +24,6 @@ import org.gradle.api.specs.Specs;
 
 import java.util.Collection;
 
-/**
- * by Szczepan Faber, created at: 10/12/12
- */
 public class ClosureToSpecNotationParser<T> implements NotationParser<Spec<T>> {
     public Spec<T> parseNotation(Object notation) throws UnsupportedNotationException {
         if (notation instanceof Closure) {
diff --git a/subprojects/core/src/main/groovy/org/gradle/api/internal/notations/parsers/CompositeNotationParser.java b/subprojects/core/src/main/groovy/org/gradle/api/internal/notations/parsers/CompositeNotationParser.java
index db00741..9b0d35a 100644
--- a/subprojects/core/src/main/groovy/org/gradle/api/internal/notations/parsers/CompositeNotationParser.java
+++ b/subprojects/core/src/main/groovy/org/gradle/api/internal/notations/parsers/CompositeNotationParser.java
@@ -21,9 +21,6 @@ import org.gradle.api.internal.notations.api.UnsupportedNotationException;
 
 import java.util.Collection;
 
-/**
- * by Szczepan Faber, created at: 11/10/11
- */
 public class CompositeNotationParser<T> implements NotationParser<T> {
 
     private final Collection<NotationParser<? extends T>> delegates;
diff --git a/subprojects/core/src/main/groovy/org/gradle/api/internal/notations/parsers/ErrorHandlingNotationParser.java b/subprojects/core/src/main/groovy/org/gradle/api/internal/notations/parsers/ErrorHandlingNotationParser.java
index 527c1d4..74c8cb2 100644
--- a/subprojects/core/src/main/groovy/org/gradle/api/internal/notations/parsers/ErrorHandlingNotationParser.java
+++ b/subprojects/core/src/main/groovy/org/gradle/api/internal/notations/parsers/ErrorHandlingNotationParser.java
@@ -26,9 +26,6 @@ import java.util.Collection;
 import java.util.Formatter;
 import java.util.List;
 
-/**
- * by Szczepan Faber, created at: 11/8/11
- */
 public class ErrorHandlingNotationParser<T> implements NotationParser<T> {
     private final String targetTypeDisplayName;
     private final String invalidNotationMessage;
diff --git a/subprojects/core/src/main/groovy/org/gradle/api/internal/notations/parsers/JustReturningParser.java b/subprojects/core/src/main/groovy/org/gradle/api/internal/notations/parsers/JustReturningParser.java
index 7b7efc6..a159a64 100644
--- a/subprojects/core/src/main/groovy/org/gradle/api/internal/notations/parsers/JustReturningParser.java
+++ b/subprojects/core/src/main/groovy/org/gradle/api/internal/notations/parsers/JustReturningParser.java
@@ -20,9 +20,6 @@ import org.gradle.api.internal.notations.api.UnsupportedNotationException;
 
 import java.util.Collection;
 
-/**
- * by Szczepan Faber, created at: 11/8/11
- */
 public class JustReturningParser<T> implements NotationParser<T> {
 
     private final Class<T> passThroughType;
diff --git a/subprojects/core/src/main/groovy/org/gradle/api/internal/notations/parsers/MapNotationParser.java b/subprojects/core/src/main/groovy/org/gradle/api/internal/notations/parsers/MapNotationParser.java
index 834cbeb..e0b2619 100644
--- a/subprojects/core/src/main/groovy/org/gradle/api/internal/notations/parsers/MapNotationParser.java
+++ b/subprojects/core/src/main/groovy/org/gradle/api/internal/notations/parsers/MapNotationParser.java
@@ -85,7 +85,7 @@ public abstract class MapNotationParser<T> extends TypedNotationParser<Map, T> i
             params[i] = value;
         }
         if (!missing.isEmpty()) {
-            //TODO SF below could be better.
+            //below could be better.
             //Throwing InvalidUserDataException here means that useful context information (including candidate formats, etc.) is not presented to the user
             throw new InvalidUserDataException(String.format("Required keys %s are missing from map %s.", missing, values));
         }
diff --git a/subprojects/core/src/main/groovy/org/gradle/api/internal/notations/parsers/NormalizedTimeUnit.java b/subprojects/core/src/main/groovy/org/gradle/api/internal/notations/parsers/NormalizedTimeUnit.java
index 0cf3327..8fba2e4 100644
--- a/subprojects/core/src/main/groovy/org/gradle/api/internal/notations/parsers/NormalizedTimeUnit.java
+++ b/subprojects/core/src/main/groovy/org/gradle/api/internal/notations/parsers/NormalizedTimeUnit.java
@@ -18,9 +18,6 @@ package org.gradle.api.internal.notations.parsers;
 
 import java.util.concurrent.TimeUnit;
 
-/**
- * by Szczepan Faber, created at: 2/18/13
- */
 public class NormalizedTimeUnit {
 
     private int value;
diff --git a/subprojects/core/src/main/groovy/org/gradle/api/internal/notations/parsers/TimeUnitsParser.java b/subprojects/core/src/main/groovy/org/gradle/api/internal/notations/parsers/TimeUnitsParser.java
index e37616f..3fa4b4c 100644
--- a/subprojects/core/src/main/groovy/org/gradle/api/internal/notations/parsers/TimeUnitsParser.java
+++ b/subprojects/core/src/main/groovy/org/gradle/api/internal/notations/parsers/TimeUnitsParser.java
@@ -22,9 +22,6 @@ import java.util.concurrent.TimeUnit;
 
 import static org.gradle.api.internal.notations.parsers.NormalizedTimeUnit.millis;
 
-/**
- * by Szczepan Faber, created at: 2/12/13
- */
 public class TimeUnitsParser {
 
     public NormalizedTimeUnit parseNotation(CharSequence notation, int value) {
diff --git a/subprojects/core/src/main/groovy/org/gradle/api/internal/notations/parsers/TypedNotationParser.java b/subprojects/core/src/main/groovy/org/gradle/api/internal/notations/parsers/TypedNotationParser.java
index 627c6a5..e1689ad 100644
--- a/subprojects/core/src/main/groovy/org/gradle/api/internal/notations/parsers/TypedNotationParser.java
+++ b/subprojects/core/src/main/groovy/org/gradle/api/internal/notations/parsers/TypedNotationParser.java
@@ -16,14 +16,12 @@
 
 package org.gradle.api.internal.notations.parsers;
 
+import org.gradle.api.internal.notations.TypeInfo;
 import org.gradle.api.internal.notations.api.NotationParser;
 import org.gradle.api.internal.notations.api.UnsupportedNotationException;
 
 import java.util.Collection;
 
-/**
- * by Szczepan Faber, created at: 11/9/11
- */
 public abstract class TypedNotationParser<N, T> implements NotationParser<T> {
 
     private final Class<N> typeToken;
@@ -33,6 +31,11 @@ public abstract class TypedNotationParser<N, T> implements NotationParser<T> {
         this.typeToken = typeToken;
     }
 
+    public TypedNotationParser(TypeInfo<N> typeToken) {
+        assert typeToken != null : "typeToken cannot be null";
+        this.typeToken = typeToken.getTargetType();
+    }
+
     public void describe(Collection<String> candidateFormats) {
         candidateFormats.add(String.format("Instances of %s.", typeToken.getSimpleName()));
     }
diff --git a/subprojects/core/src/main/groovy/org/gradle/api/internal/plugins/DefaultConvention.java b/subprojects/core/src/main/groovy/org/gradle/api/internal/plugins/DefaultConvention.java
index 5584d4c..f0c8e26 100644
--- a/subprojects/core/src/main/groovy/org/gradle/api/internal/plugins/DefaultConvention.java
+++ b/subprojects/core/src/main/groovy/org/gradle/api/internal/plugins/DefaultConvention.java
@@ -22,17 +22,14 @@ import org.gradle.api.Action;
 import org.gradle.api.GradleException;
 import org.gradle.api.internal.BeanDynamicObject;
 import org.gradle.api.internal.DynamicObject;
-import org.gradle.internal.reflect.Instantiator;
 import org.gradle.api.plugins.Convention;
 import org.gradle.api.plugins.ExtraPropertiesExtension;
+import org.gradle.internal.reflect.Instantiator;
 import org.gradle.util.DeprecationLogger;
 
 import java.util.*;
 
-/**
- * @author Hans Dockter
- */
-public class DefaultConvention implements Convention {
+public class DefaultConvention implements Convention, ExtensionContainerInternal {
 
     private final Map<String, Object> plugins = new LinkedHashMap<String, Object>();
     private final DefaultConvention.ExtensionsDynamicObject extensionsDynamicObject = new ExtensionsDynamicObject();
@@ -141,6 +138,10 @@ public class DefaultConvention implements Convention {
         extensionsStorage.configureExtension(type, action);
     }
 
+    public Map<String, Object> getAsMap() {
+        return extensionsStorage.getAsMap();
+    }
+
     public Object propertyMissing(String name) {
         return getByName(name);
     }
diff --git a/subprojects/core/src/main/groovy/org/gradle/api/internal/plugins/DefaultObjectConfigurationAction.java b/subprojects/core/src/main/groovy/org/gradle/api/internal/plugins/DefaultObjectConfigurationAction.java
index 31f7524..90a345a 100755
--- a/subprojects/core/src/main/groovy/org/gradle/api/internal/plugins/DefaultObjectConfigurationAction.java
+++ b/subprojects/core/src/main/groovy/org/gradle/api/internal/plugins/DefaultObjectConfigurationAction.java
@@ -17,9 +17,9 @@
 package org.gradle.api.internal.plugins;
 
 import org.gradle.api.Plugin;
-import org.gradle.api.Project;
 import org.gradle.api.internal.file.FileResolver;
 import org.gradle.api.plugins.ObjectConfigurationAction;
+import org.gradle.api.plugins.PluginAware;
 import org.gradle.configuration.ScriptPlugin;
 import org.gradle.configuration.ScriptPluginFactory;
 import org.gradle.groovy.scripts.UriScriptSource;
@@ -85,22 +85,22 @@ public class DefaultObjectConfigurationAction implements ObjectConfigurationActi
 
     private void applyPlugin(Class<? extends Plugin> pluginClass) {
         for (Object target : targets) {
-            if (target instanceof Project) {
-                Project project = (Project) target;
-                project.getPlugins().apply(pluginClass);
+            if (target instanceof PluginAware) {
+                PluginAware pluginAware = (PluginAware) target;
+                pluginAware.getPlugins().apply(pluginClass);
             } else {
-                throw new UnsupportedOperationException(String.format("Cannot apply plugin of class '%s' to '%s' (class: %s) as it is not a Project", pluginClass.getName(), target.toString(), target.getClass().getName()));
+                throw new UnsupportedOperationException(String.format("Cannot apply plugin of class '%s' to '%s' (class: %s) as it does not implement PluginAware", pluginClass.getName(), target.toString(), target.getClass().getName()));
             }
         }
     }
 
     private void applyPlugin(String pluginId) {
         for (Object target : targets) {
-            if (target instanceof Project) {
-                Project project = (Project) target;
-                project.getPlugins().apply(pluginId);
+            if (target instanceof PluginAware) {
+                PluginAware pluginAware = (PluginAware) target;
+                pluginAware.getPlugins().apply(pluginId);
             } else {
-                throw new UnsupportedOperationException(String.format("Cannot apply plugin with id '%s' to '%s' (class: %s) as it is not a Project", pluginId, target.toString(), target.getClass().getName()));
+                throw new UnsupportedOperationException(String.format("Cannot apply plugin with id '%s' to '%s' (class: %s) as it does not implement PluginAware", pluginId, target.toString(), target.getClass().getName()));
             }
         }
     }
diff --git a/subprojects/core/src/main/groovy/org/gradle/api/internal/plugins/DefaultPluginContainer.java b/subprojects/core/src/main/groovy/org/gradle/api/internal/plugins/DefaultPluginContainer.java
new file mode 100644
index 0000000..1e88e9c
--- /dev/null
+++ b/subprojects/core/src/main/groovy/org/gradle/api/internal/plugins/DefaultPluginContainer.java
@@ -0,0 +1,107 @@
+/*
+ * Copyright 2010 the original author or authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.gradle.api.internal.plugins;
+
+import org.gradle.api.Plugin;
+import org.gradle.api.plugins.PluginAware;
+import org.gradle.api.plugins.PluginContainer;
+import org.gradle.api.plugins.UnknownPluginException;
+
+public class DefaultPluginContainer<T extends PluginAware> extends DefaultPluginCollection<Plugin> implements PluginContainer {
+    private PluginRegistry pluginRegistry;
+    private final T pluginAware;
+
+    public DefaultPluginContainer(PluginRegistry pluginRegistry, T pluginAware) {
+        super(Plugin.class);
+        this.pluginRegistry = pluginRegistry;
+        this.pluginAware = pluginAware;
+    }
+
+    public Plugin apply(String id) {
+        return addPluginInternal(getTypeForId(id));
+    }
+
+    public <T extends Plugin> T apply(Class<T> type) {
+        return addPluginInternal(type);
+    }
+
+    public boolean hasPlugin(String id) {
+        return findPlugin(id) != null;
+    }
+
+    public boolean hasPlugin(Class<? extends Plugin> type) {
+        return findPlugin(type) != null;
+    }
+
+    public Plugin findPlugin(String id) {
+        try {
+            return findPlugin(getTypeForId(id));
+        } catch (UnknownPluginException e) {
+            return null;
+        }
+    }
+
+    public <T extends Plugin> T findPlugin(Class<T> type) {
+        for (Plugin plugin : this) {
+            if (plugin.getClass().equals(type)) {
+                return type.cast(plugin);
+            }
+        }
+        return null;
+    }
+
+    private <T extends Plugin> T addPluginInternal(Class<T> type) {
+        if (findPlugin(type) == null) {
+            Plugin plugin = providePlugin(type);
+            add(plugin);
+        }
+        return type.cast(findPlugin(type));
+    }
+
+    public Plugin getPlugin(String id) {
+        Plugin plugin = findPlugin(id);
+        if (plugin == null) {
+            throw new UnknownPluginException("Plugin with id " + id + " has not been used.");
+        }
+        return plugin;
+    }
+
+    public Plugin getAt(String id) throws UnknownPluginException {
+        return getPlugin(id);
+    }
+
+    public <T extends Plugin> T getAt(Class<T> type) throws UnknownPluginException {
+        return getPlugin(type);
+    }
+
+    public <T extends Plugin> T getPlugin(Class<T> type) throws UnknownPluginException {
+        Plugin plugin = findPlugin(type);
+        if (plugin == null) {
+            throw new UnknownPluginException("Plugin with type " + type + " has not been used.");
+        }
+        return type.cast(plugin);
+    }
+
+    protected Class<? extends Plugin> getTypeForId(String id) {
+        return pluginRegistry.getTypeForId(id);
+    }
+
+    private Plugin<T> providePlugin(Class<? extends Plugin> type) {
+        Plugin<T> plugin = pluginRegistry.loadPlugin(type);
+        plugin.apply(pluginAware);
+        return plugin;
+    }
+}
diff --git a/subprojects/core/src/main/groovy/org/gradle/api/internal/plugins/DefaultPluginRegistry.java b/subprojects/core/src/main/groovy/org/gradle/api/internal/plugins/DefaultPluginRegistry.java
index fc0c279..cb72ee5 100644
--- a/subprojects/core/src/main/groovy/org/gradle/api/internal/plugins/DefaultPluginRegistry.java
+++ b/subprojects/core/src/main/groovy/org/gradle/api/internal/plugins/DefaultPluginRegistry.java
@@ -29,10 +29,6 @@ import java.util.HashMap;
 import java.util.Map;
 import java.util.Properties;
 
-/**
- * @author Hans Dockter
- */
-
 public class DefaultPluginRegistry implements PluginRegistry {
     private final Map<String, Class<? extends Plugin>> idMappings = new HashMap<String, Class<? extends Plugin>>();
     private final DefaultPluginRegistry parent;
diff --git a/subprojects/core/src/main/groovy/org/gradle/api/internal/plugins/DefaultProjectsPluginContainer.java b/subprojects/core/src/main/groovy/org/gradle/api/internal/plugins/DefaultProjectsPluginContainer.java
deleted file mode 100644
index a6fe69a..0000000
--- a/subprojects/core/src/main/groovy/org/gradle/api/internal/plugins/DefaultProjectsPluginContainer.java
+++ /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.internal.plugins;
-
-import org.gradle.api.Plugin;
-import org.gradle.api.Project;
-import org.gradle.api.plugins.PluginContainer;
-import org.gradle.api.plugins.UnknownPluginException;
-
-/**
- * @author Hans Dockter
- */
-public class DefaultProjectsPluginContainer extends DefaultPluginCollection<Plugin> implements PluginContainer {
-    private PluginRegistry pluginRegistry;
-    private final Project project;
-
-    public DefaultProjectsPluginContainer(PluginRegistry pluginRegistry, Project project) {
-        super(Plugin.class);
-        this.pluginRegistry = pluginRegistry;
-        this.project = project;
-    }
-
-    public Plugin apply(String id) {
-        return addPluginInternal(getTypeForId(id));
-    }
-
-    public <T extends Plugin> T apply(Class<T> type) {
-        return addPluginInternal(type);
-    }
-
-    public boolean hasPlugin(String id) {
-        return findPlugin(id) != null;
-    }
-
-    public boolean hasPlugin(Class<? extends Plugin> type) {
-        return findPlugin(type) != null;
-    }
-
-    public Plugin findPlugin(String id) {
-        try {
-            return findPlugin(getTypeForId(id));
-        } catch (UnknownPluginException e) {
-            return null;
-        }
-    }
-
-    public <T extends Plugin> T findPlugin(Class<T> type) {
-        for (Plugin plugin : this) {
-            if (plugin.getClass().equals(type)) {
-                return type.cast(plugin);
-            }
-        }
-        return null;
-    }
-
-    private <T extends Plugin> T addPluginInternal(Class<T> type) {
-        if (findPlugin(type) == null) {
-            Plugin plugin = providePlugin(type);
-            add(plugin);
-        }
-        return type.cast(findPlugin(type));
-    }
-
-    public Plugin getPlugin(String id) {
-        Plugin plugin = findPlugin(id);
-        if (plugin == null) {
-            throw new UnknownPluginException("Plugin with id " + id + " has not been used.");
-        }
-        return plugin;
-    }
-
-    public Plugin getAt(String id) throws UnknownPluginException {
-        return getPlugin(id);
-    }
-
-    public <T extends Plugin> T getAt(Class<T> type) throws UnknownPluginException {
-        return getPlugin(type);
-    }
-
-    public <T extends Plugin> T getPlugin(Class<T> type) throws UnknownPluginException {
-        Plugin plugin = findPlugin(type);
-        if (plugin == null) {
-            throw new UnknownPluginException("Plugin with type " + type + " has not been used.");
-        }
-        return type.cast(plugin);
-    }
-
-    protected Class<? extends Plugin> getTypeForId(String id) {
-        return pluginRegistry.getTypeForId(id);
-    }
-
-    private Plugin<Project> providePlugin(Class<? extends Plugin> type) {
-        Plugin<Project> plugin = pluginRegistry.loadPlugin(type);
-        plugin.apply(project);
-        return plugin;
-    }
-}
diff --git a/subprojects/core/src/main/groovy/org/gradle/api/internal/plugins/ExtensionContainerInternal.java b/subprojects/core/src/main/groovy/org/gradle/api/internal/plugins/ExtensionContainerInternal.java
new file mode 100644
index 0000000..1236d4d
--- /dev/null
+++ b/subprojects/core/src/main/groovy/org/gradle/api/internal/plugins/ExtensionContainerInternal.java
@@ -0,0 +1,29 @@
+/*
+ * Copyright 2013 the original author or authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.gradle.api.internal.plugins;
+
+import org.gradle.api.plugins.ExtensionContainer;
+
+import java.util.Map;
+
+public interface ExtensionContainerInternal extends ExtensionContainer {
+    /**
+     * Provides access to all known extensions.
+     * @return A map of extensions, keyed by name.
+     */
+    Map<String, Object> getAsMap();
+}
diff --git a/subprojects/core/src/main/groovy/org/gradle/api/internal/plugins/ExtensionsStorage.java b/subprojects/core/src/main/groovy/org/gradle/api/internal/plugins/ExtensionsStorage.java
index 00372ba..e3a4026 100644
--- a/subprojects/core/src/main/groovy/org/gradle/api/internal/plugins/ExtensionsStorage.java
+++ b/subprojects/core/src/main/groovy/org/gradle/api/internal/plugins/ExtensionsStorage.java
@@ -22,6 +22,7 @@ import org.gradle.api.InvalidUserDataException;
 import org.gradle.api.UnknownDomainObjectException;
 import org.gradle.api.internal.ClosureBackedAction;
 import org.gradle.api.plugins.DeferredConfigurable;
+import org.gradle.internal.UncheckedException;
 import org.gradle.listener.ActionBroadcast;
 import org.gradle.listener.ListenerNotificationException;
 
@@ -30,9 +31,6 @@ import java.util.LinkedList;
 import java.util.List;
 import java.util.Map;
 
-/**
- * @author Szczepan Faber, created at: 6/24/11
- */
 public class ExtensionsStorage {
     private final Map<String, ExtensionHolder> extensions = new LinkedHashMap<String, ExtensionHolder>();
 
@@ -40,7 +38,7 @@ public class ExtensionsStorage {
         if (extensions.containsKey(name)) {
             throw new IllegalArgumentException(String.format("Cannot add extension with name '%s', as there is an extension already registered with that name.", name));
         }
-        extensions.put(name, wrap(extension));
+        extensions.put(name, wrap(name, extension));
     }
 
     public boolean hasExtension(String name) {
@@ -81,11 +79,13 @@ public class ExtensionsStorage {
     }
 
     public <T> T findByType(Class<T> type) {
+        ExtensionHolder<T> holder;
         try {
-            return getHolderByType(type).get();
+            holder = getHolderByType(type);
         } catch (UnknownDomainObjectException e) {
             return null;
         }
+        return holder.get();
     }
 
     private <T> ExtensionHolder<T> getHolderByType(Class<T> type) {
@@ -112,9 +112,9 @@ public class ExtensionsStorage {
         return extensionHolder == null ? null : extensionHolder.get();
     }
 
-    private <T> ExtensionHolder<T> wrap(T extension) {
+    private <T> ExtensionHolder<T> wrap(String name, T extension) {
         if (isDeferredConfigurable(extension)) {
-            return new DeferredConfigurableExtensionHolder<T>(extension);
+            return new DeferredConfigurableExtensionHolder<T>(name, extension);
         }
         return new ExtensionHolder<T>(extension);
     }
@@ -149,11 +149,14 @@ public class ExtensionsStorage {
     }
 
     private static class DeferredConfigurableExtensionHolder<T> extends ExtensionHolder<T> {
+        private final String name;
         private ActionBroadcast<T> actions = new ActionBroadcast<T>();
         private boolean configured;
+        private Throwable configureFailure;
 
-        private DeferredConfigurableExtensionHolder(T extension) {
+        public DeferredConfigurableExtensionHolder(String name, T extension) {
             super(extension);
+            this.name = name;
         }
 
         public T get() {
@@ -169,7 +172,7 @@ public class ExtensionsStorage {
 
         private void configureLater(Action<? super T> action) {
             if (configured) {
-                throw new IllegalStateException("The 'publishing' extension is already configured");
+                throw new InvalidUserDataException(String.format("Cannot configure the '%s' extension after it has been accessed.", name));
             }
             actions.add(action);
         }
@@ -180,8 +183,13 @@ public class ExtensionsStorage {
                 try {
                     actions.execute(extension);
                 } catch (ListenerNotificationException e) {
-                    throw new InvalidUserDataException("A problem occurred configuring the 'publishing' extension", e.getCause());
+                    configureFailure = e.getCause();
                 }
+                actions = null;
+            }
+
+            if (configureFailure != null) {
+                throw UncheckedException.throwAsUncheckedException(configureFailure);
             }
         }
 
diff --git a/subprojects/core/src/main/groovy/org/gradle/api/internal/plugins/PluginRegistry.java b/subprojects/core/src/main/groovy/org/gradle/api/internal/plugins/PluginRegistry.java
index b14bd5f..45d0277 100644
--- a/subprojects/core/src/main/groovy/org/gradle/api/internal/plugins/PluginRegistry.java
+++ b/subprojects/core/src/main/groovy/org/gradle/api/internal/plugins/PluginRegistry.java
@@ -21,9 +21,6 @@ import org.gradle.api.plugins.PluginInstantiationException;
 import org.gradle.api.plugins.UnknownPluginException;
 import org.gradle.internal.reflect.Instantiator;
 
-/**
- * @author Hans Dockter
- */
 public interface PluginRegistry {
     <T extends Plugin> T loadPlugin(Class<T> pluginClass) throws PluginInstantiationException;
 
diff --git a/subprojects/core/src/main/groovy/org/gradle/api/internal/project/AbstractPluginAware.java b/subprojects/core/src/main/groovy/org/gradle/api/internal/project/AbstractPluginAware.java
new file mode 100644
index 0000000..b1e2630
--- /dev/null
+++ b/subprojects/core/src/main/groovy/org/gradle/api/internal/project/AbstractPluginAware.java
@@ -0,0 +1,45 @@
+/*
+ * Copyright 2013 the original author or authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY 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 groovy.lang.Closure;
+import org.gradle.api.internal.file.FileResolver;
+import org.gradle.api.internal.plugins.DefaultObjectConfigurationAction;
+import org.gradle.api.plugins.PluginAware;
+import org.gradle.configuration.ScriptPluginFactory;
+import org.gradle.util.ConfigureUtil;
+
+import java.util.Map;
+
+abstract public class AbstractPluginAware implements PluginAware {
+
+    public void apply(Closure closure) {
+        DefaultObjectConfigurationAction action = new DefaultObjectConfigurationAction(getFileResolver(), getScriptPluginFactory(), this);
+        ConfigureUtil.configure(closure, action);
+        action.execute();
+    }
+
+    public void apply(Map<String, ?> options) {
+        DefaultObjectConfigurationAction action = new DefaultObjectConfigurationAction(getFileResolver(), getScriptPluginFactory(), this);
+        ConfigureUtil.configureByMap(options, action);
+        action.execute();
+    }
+
+    protected abstract FileResolver getFileResolver();
+
+    protected abstract ScriptPluginFactory getScriptPluginFactory();
+}
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 4162959..1676cb5 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
@@ -35,24 +35,26 @@ import org.gradle.api.internal.artifacts.configurations.ConfigurationContainerIn
 import org.gradle.api.internal.artifacts.configurations.DependencyMetaDataProvider;
 import org.gradle.api.internal.file.FileOperations;
 import org.gradle.api.internal.file.FileResolver;
+import org.gradle.api.internal.file.copy.CopySpecInternal;
 import org.gradle.api.internal.initialization.ScriptClassLoaderProvider;
-import org.gradle.api.internal.plugins.DefaultObjectConfigurationAction;
+import org.gradle.api.internal.plugins.ExtensionContainerInternal;
 import org.gradle.api.internal.tasks.TaskContainerInternal;
 import org.gradle.api.logging.Logger;
 import org.gradle.api.logging.Logging;
 import org.gradle.api.logging.LoggingManager;
 import org.gradle.api.plugins.Convention;
-import org.gradle.api.plugins.ExtensionContainer;
 import org.gradle.api.plugins.PluginContainer;
 import org.gradle.api.resources.ResourceHandler;
 import org.gradle.api.tasks.Directory;
 import org.gradle.api.tasks.WorkResult;
-import org.gradle.configuration.ProjectEvaluator;
 import org.gradle.configuration.ScriptPlugin;
 import org.gradle.configuration.ScriptPluginFactory;
+import org.gradle.configuration.project.ProjectConfigurationActionContainer;
+import org.gradle.configuration.project.ProjectEvaluator;
 import org.gradle.groovy.scripts.ScriptSource;
 import org.gradle.internal.Factory;
 import org.gradle.internal.reflect.Instantiator;
+import org.gradle.internal.service.scopes.ServiceRegistryFactory;
 import org.gradle.listener.ClosureBackedMethodInvocationDispatch;
 import org.gradle.listener.ListenerBroadcast;
 import org.gradle.logging.LoggingManagerInternal;
@@ -71,10 +73,7 @@ import static java.util.Collections.singletonMap;
 import static org.gradle.util.GUtil.addMaps;
 import static org.gradle.util.GUtil.isTrue;
 
-/**
- * @author Hans Dockter
- */
-public abstract class AbstractProject implements ProjectInternal, DynamicObjectAware {
+public abstract class AbstractProject extends AbstractPluginAware implements ProjectInternal, DynamicObjectAware {
     private static Logger buildLogger = Logging.getLogger(Project.class);
     private ServiceRegistryFactory services;
 
@@ -124,7 +123,7 @@ public abstract class AbstractProject implements ProjectInternal, DynamicObjectA
 
     private TaskContainerInternal implicitTasksContainer;
 
-    private IProjectRegistry<ProjectInternal> projectRegistry;
+    private ProjectRegistry<ProjectInternal> projectRegistry;
 
     private DependencyHandler dependencyHandler;
 
@@ -146,9 +145,12 @@ public abstract class AbstractProject implements ProjectInternal, DynamicObjectA
 
     private ExtensibleDynamicObject extensibleDynamicObject;
 
+    private ProjectConfigurationActionContainer configurationActions;
+
     private String description;
 
     private final Path path;
+    private ScriptPluginFactory scriptPluginFactory;
 
     public AbstractProject(String name,
                            ProjectInternal parent,
@@ -189,9 +191,11 @@ public abstract class AbstractProject implements ProjectInternal, DynamicObjectA
         dependencyHandler = services.get(DependencyHandler.class);
         scriptHandler = services.get(ScriptHandler.class);
         scriptClassLoaderProvider = services.get(ScriptClassLoaderProvider.class);
-        projectRegistry = services.get(IProjectRegistry.class);
+        projectRegistry = services.get(ProjectRegistry.class);
         loggingManager = services.get(LoggingManagerInternal.class);
         softwareComponentContainer = services.get(SoftwareComponentContainer.class);
+        scriptPluginFactory = services.get(ScriptPluginFactory.class);
+        configurationActions = services.get(ProjectConfigurationActionContainer.class);
 
         extensibleDynamicObject = new ExtensibleDynamicObject(this, services.get(Instantiator.class));
         if (parent != null) {
@@ -379,7 +383,7 @@ public abstract class AbstractProject implements ProjectInternal, DynamicObjectA
         return depth;
     }
 
-    public IProjectRegistry<ProjectInternal> getProjectRegistry() {
+    public ProjectRegistry<ProjectInternal> getProjectRegistry() {
         return projectRegistry;
     }
 
@@ -492,7 +496,7 @@ public abstract class AbstractProject implements ProjectInternal, DynamicObjectA
         Map<String, Object> allArgs = new HashMap<String, Object>(args);
         allArgs.put(Task.TASK_NAME, name);
         allArgs.put(Task.TASK_ACTION, action);
-        return taskContainer.add(allArgs);
+        return taskContainer.create(allArgs);
     }
 
     public Task createTask(Map<String, ?> args, String name, Action<? super Task> action) {
@@ -502,7 +506,7 @@ public abstract class AbstractProject implements ProjectInternal, DynamicObjectA
         if (action != null) {
             allArgs.put(Task.TASK_ACTION, action);
         }
-        return taskContainer.add(allArgs);
+        return taskContainer.create(allArgs);
     }
 
     private void warnCreateTaskDeprecated() {
@@ -726,7 +730,7 @@ public abstract class AbstractProject implements ProjectInternal, DynamicObjectA
             } else if (task != null) {
                 throw new InvalidUserDataException(String.format("Cannot add directory task '%s' as a non-directory task with this name already exists.", name));
             } else {
-                dirTask = taskContainer.add(name, Directory.class);
+                dirTask = taskContainer.create(name, Directory.class);
             }
         }
         return dirTask;
@@ -812,10 +816,18 @@ public abstract class AbstractProject implements ProjectInternal, DynamicObjectA
         return fileOperations.copy(closure);
     }
 
+    public WorkResult sync(Action<? super CopySpec> action) {
+        return fileOperations.sync(action);
+    }
+
     public CopySpec copySpec(Closure closure) {
         return fileOperations.copySpec(closure);
     }
 
+    public CopySpecInternal copySpec(Action<? super CopySpec> action) {
+        return fileOperations.copySpec(action);
+    }
+
     public ExecResult javaexec(Closure closure) {
         return processOperations.javaexec(closure);
     }
@@ -832,20 +844,6 @@ public abstract class AbstractProject implements ProjectInternal, DynamicObjectA
         return getServices().get(DependencyMetaDataProvider.class).getModule();
     }
 
-    public void apply(Closure closure) {
-        DefaultObjectConfigurationAction action = new DefaultObjectConfigurationAction(fileResolver, services.get(
-                ScriptPluginFactory.class), this);
-        configure(action, closure);
-        action.execute();
-    }
-
-    public void apply(Map<String, ?> options) {
-        DefaultObjectConfigurationAction action = new DefaultObjectConfigurationAction(fileResolver, services.get(
-                ScriptPluginFactory.class), this);
-        ConfigureUtil.configureByMap(options, action);
-        action.execute();
-    }
-
     public AntBuilder ant(Closure configureClosure) {
         return ConfigureUtil.configure(configureClosure, getAnt());
     }
@@ -894,15 +892,15 @@ public abstract class AbstractProject implements ProjectInternal, DynamicObjectA
     }
 
     public Task task(String task) {
-        return taskContainer.add(task);
+        return taskContainer.create(task);
     }
 
     public Task task(Object task) {
-        return taskContainer.add(task.toString());
+        return taskContainer.create(task.toString());
     }
 
     public Task task(String task, Closure configureClosure) {
-        return taskContainer.add(task).configure(configureClosure);
+        return taskContainer.create(task).configure(configureClosure);
     }
 
     public Task task(Object task, Closure configureClosure) {
@@ -910,7 +908,7 @@ public abstract class AbstractProject implements ProjectInternal, DynamicObjectA
     }
 
     public Task task(Map options, String task) {
-        return taskContainer.add(addMaps(options, singletonMap(Task.TASK_NAME, task)));
+        return taskContainer.create(addMaps(options, singletonMap(Task.TASK_NAME, task)));
     }
 
     public Task task(Map options, Object task) {
@@ -918,13 +916,22 @@ public abstract class AbstractProject implements ProjectInternal, DynamicObjectA
     }
 
     public Task task(Map options, String task, Closure configureClosure) {
-        return taskContainer.add(addMaps(options, singletonMap(Task.TASK_NAME, task))).configure(configureClosure);
+        return taskContainer.create(addMaps(options, singletonMap(Task.TASK_NAME, task))).configure(configureClosure);
     }
 
     public Task task(Map options, Object task, Closure configureClosure) {
         return task(options, task.toString(), configureClosure);
     }
 
+    public ProjectConfigurationActionContainer getConfigurationActions() {
+        return configurationActions;
+    }
+
+    @Override
+    protected ScriptPluginFactory getScriptPluginFactory() {
+        return scriptPluginFactory;
+    }
+
     /**
      * This is called by the task creation DSL. Need to find a cleaner way to do this...
      */
@@ -947,7 +954,8 @@ public abstract class AbstractProject implements ProjectInternal, DynamicObjectA
         return instantiator.newInstance(FactoryNamedDomainObjectContainer.class, type, instantiator, new DynamicPropertyNamer(), factoryClosure);
     }
 
-    public ExtensionContainer getExtensions() {
-        return getConvention();
+    public ExtensionContainerInternal getExtensions() {
+        return (ExtensionContainerInternal) getConvention();
     }
+
 }
diff --git a/subprojects/core/src/main/groovy/org/gradle/api/internal/project/DefaultAntBuilder.groovy b/subprojects/core/src/main/groovy/org/gradle/api/internal/project/DefaultAntBuilder.groovy
index 9cd1aa6..956df16 100644
--- a/subprojects/core/src/main/groovy/org/gradle/api/internal/project/DefaultAntBuilder.groovy
+++ b/subprojects/core/src/main/groovy/org/gradle/api/internal/project/DefaultAntBuilder.groovy
@@ -88,7 +88,7 @@ public class DefaultAntBuilder extends BasicAntBuilder {
         newAntTargets.removeAll(existingAntTargets)
         newAntTargets.each {name ->
             Target target = antProject.targets[name]
-            AntTarget task = gradleProject.tasks.add(target.name, AntTarget)
+            AntTarget task = gradleProject.tasks.create(target.name, AntTarget)
             task.target = target
             task.baseDir = baseDir
         }
diff --git a/subprojects/core/src/main/groovy/org/gradle/api/internal/project/DefaultAntBuilderFactory.java b/subprojects/core/src/main/groovy/org/gradle/api/internal/project/DefaultAntBuilderFactory.java
index 6b39102..fc9ef43 100644
--- a/subprojects/core/src/main/groovy/org/gradle/api/internal/project/DefaultAntBuilderFactory.java
+++ b/subprojects/core/src/main/groovy/org/gradle/api/internal/project/DefaultAntBuilderFactory.java
@@ -20,9 +20,6 @@ import org.gradle.api.AntBuilder;
 import org.gradle.api.Project;
 import org.gradle.internal.Factory;
 
-/**
- * @author Hans Dockter
- */
 public class DefaultAntBuilderFactory implements Factory<AntBuilder> {
     private final BuildListener buildListener;
     private final Project project;
diff --git a/subprojects/core/src/main/groovy/org/gradle/api/internal/project/DefaultIsolatedAntBuilder.groovy b/subprojects/core/src/main/groovy/org/gradle/api/internal/project/DefaultIsolatedAntBuilder.groovy
index 9782958..ce7ebd0 100644
--- a/subprojects/core/src/main/groovy/org/gradle/api/internal/project/DefaultIsolatedAntBuilder.groovy
+++ b/subprojects/core/src/main/groovy/org/gradle/api/internal/project/DefaultIsolatedAntBuilder.groovy
@@ -18,6 +18,10 @@ package org.gradle.api.internal.project
 import org.gradle.api.internal.ClassPathRegistry
 import org.gradle.api.internal.project.ant.AntLoggingAdapter
 import org.gradle.api.internal.project.ant.BasicAntBuilder
+import org.gradle.internal.classloader.ClassLoaderFactory
+import org.gradle.internal.classloader.FilteringClassLoader
+import org.gradle.internal.classloader.MultiParentClassLoader
+import org.gradle.internal.classloader.MutableURLClassLoader
 import org.gradle.util.*
 import org.gradle.internal.jvm.Jvm
 import org.gradle.internal.classpath.DefaultClassPath
diff --git a/subprojects/core/src/main/groovy/org/gradle/api/internal/project/DefaultProject.java b/subprojects/core/src/main/groovy/org/gradle/api/internal/project/DefaultProject.java
index 9981c87..5148f39 100644
--- a/subprojects/core/src/main/groovy/org/gradle/api/internal/project/DefaultProject.java
+++ b/subprojects/core/src/main/groovy/org/gradle/api/internal/project/DefaultProject.java
@@ -19,6 +19,7 @@ package org.gradle.api.internal.project;
 import org.gradle.api.internal.GradleInternal;
 import org.gradle.api.internal.NoConventionMapping;
 import org.gradle.groovy.scripts.ScriptSource;
+import org.gradle.internal.service.scopes.ServiceRegistryFactory;
 
 import java.io.File;
 
diff --git a/subprojects/core/src/main/groovy/org/gradle/api/internal/project/DefaultProjectAccessListener.java b/subprojects/core/src/main/groovy/org/gradle/api/internal/project/DefaultProjectAccessListener.java
index ab2c723..7583d3c 100644
--- a/subprojects/core/src/main/groovy/org/gradle/api/internal/project/DefaultProjectAccessListener.java
+++ b/subprojects/core/src/main/groovy/org/gradle/api/internal/project/DefaultProjectAccessListener.java
@@ -18,9 +18,6 @@ package org.gradle.api.internal.project;
 
 import org.gradle.initialization.ProjectAccessListener;
 
-/**
-* by Szczepan Faber, created at: 2/11/13
-*/
 public class DefaultProjectAccessListener implements ProjectAccessListener {
 
     public void beforeRequestingTaskByPath(ProjectInternal targetProject) {
diff --git a/subprojects/core/src/main/groovy/org/gradle/api/internal/project/DefaultProjectRegistry.java b/subprojects/core/src/main/groovy/org/gradle/api/internal/project/DefaultProjectRegistry.java
index 38e6870..d4c0918 100644
--- a/subprojects/core/src/main/groovy/org/gradle/api/internal/project/DefaultProjectRegistry.java
+++ b/subprojects/core/src/main/groovy/org/gradle/api/internal/project/DefaultProjectRegistry.java
@@ -20,15 +20,12 @@ import org.gradle.api.specs.Spec;
 import org.gradle.util.GUtil;
 
 import java.io.File;
-import java.util.Map;
 import java.util.HashMap;
-import java.util.Set;
 import java.util.HashSet;
+import java.util.Map;
+import java.util.Set;
 
-/**
- * @author Hans Dockter
- */
-public class DefaultProjectRegistry<T extends ProjectIdentifier> implements IProjectRegistry<T> {
+public class DefaultProjectRegistry<T extends ProjectIdentifier> implements ProjectRegistry<T> {
     private Map<String, T> projects = new HashMap<String, T>();
     private Map<String, Set<T>> subProjects = new HashMap<String, Set<T>>();
 
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
deleted file mode 100755
index 1f4c673..0000000
--- a/subprojects/core/src/main/groovy/org/gradle/api/internal/project/GlobalServicesRegistry.java
+++ /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.api.internal.project;
-
-import org.gradle.StartParameter;
-import org.gradle.api.internal.*;
-import org.gradle.api.internal.classpath.DefaultModuleRegistry;
-import org.gradle.api.internal.classpath.DefaultPluginModuleRegistry;
-import org.gradle.api.internal.classpath.ModuleRegistry;
-import org.gradle.api.internal.classpath.PluginModuleRegistry;
-import org.gradle.cache.internal.*;
-import org.gradle.cli.CommandLineConverter;
-import org.gradle.initialization.ClassLoaderRegistry;
-import org.gradle.initialization.DefaultClassLoaderRegistry;
-import org.gradle.initialization.DefaultCommandLineConverter;
-import org.gradle.internal.Factory;
-import org.gradle.internal.nativeplatform.ProcessEnvironment;
-import org.gradle.internal.nativeplatform.services.NativeServices;
-import org.gradle.internal.reflect.DirectInstantiator;
-import org.gradle.internal.reflect.Instantiator;
-import org.gradle.internal.service.DefaultServiceRegistry;
-import org.gradle.internal.service.ServiceRegistry;
-import org.gradle.listener.DefaultListenerManager;
-import org.gradle.listener.ListenerManager;
-import org.gradle.logging.LoggingServiceRegistry;
-import org.gradle.messaging.remote.MessagingServer;
-import org.gradle.messaging.remote.internal.MessagingServices;
-import org.gradle.util.ClassLoaderFactory;
-import org.gradle.util.DefaultClassLoaderFactory;
-
-/**
- * Contains the services shared by all builds in a given process.
- */
-public class GlobalServicesRegistry extends DefaultServiceRegistry {
-    public GlobalServicesRegistry() {
-        this(LoggingServiceRegistry.newProcessLogging());
-    }
-
-    public GlobalServicesRegistry(ServiceRegistry loggingServices) {
-        super(loggingServices);
-        add(NativeServices.getInstance());
-    }
-
-    protected CommandLineConverter<StartParameter> createCommandLine2StartParameterConverter() {
-        return new DefaultCommandLineConverter();
-    }
-
-    protected ClassPathRegistry createClassPathRegistry() {
-        return new DefaultClassPathRegistry(new DefaultClassPathProvider(get(ModuleRegistry.class)), new DynamicModulesClassPathProvider(get(ModuleRegistry.class), get(PluginModuleRegistry.class)));
-    }
-
-    protected DefaultModuleRegistry createModuleRegistry() {
-        return new DefaultModuleRegistry();
-    }
-
-    protected DocumentationRegistry createDocumentationRegistry() {
-        return new DocumentationRegistry(get(GradleDistributionLocator.class));
-    }
-
-    protected PluginModuleRegistry createPluginModuleRegistry() {
-        return new DefaultPluginModuleRegistry(get(ModuleRegistry.class));
-    }
-
-    protected Factory<CacheFactory> createCacheFactory() {
-        return new DefaultCacheFactory(get(FileLockManager.class));
-    }
-
-    protected ClassLoaderRegistry createClassLoaderRegistry() {
-        return new DefaultClassLoaderRegistry(get(ClassPathRegistry.class), get(ClassLoaderFactory.class));
-    }
-
-    protected ListenerManager createListenerManager() {
-        return new DefaultListenerManager();
-    }
-   
-    protected ClassLoaderFactory createClassLoaderFactory() {
-        return new DefaultClassLoaderFactory();
-    }
-
-    protected MessagingServices createMessagingServices() {
-        return new MessagingServices(get(ClassLoaderRegistry.class).getPluginsClassLoader());
-    }
-
-    protected MessagingServer createMessagingServer() {
-        return get(MessagingServices.class).get(MessagingServer.class);
-    }
-
-    protected ClassGenerator createClassGenerator() {
-        return new AsmBackedClassGenerator();
-    }
-
-    protected Instantiator createInstantiator() {
-        return new ClassGeneratorBackedInstantiator(get(ClassGenerator.class), new DirectInstantiator());
-    }
-
-    protected FileLockManager createFileLockManager() {
-        return new DefaultFileLockManager(new DefaultProcessMetaDataProvider(get(ProcessEnvironment.class)));
-    }
-}
diff --git a/subprojects/core/src/main/groovy/org/gradle/api/internal/project/GradleInternalServiceRegistry.java b/subprojects/core/src/main/groovy/org/gradle/api/internal/project/GradleInternalServiceRegistry.java
deleted file mode 100644
index 996720f..0000000
--- a/subprojects/core/src/main/groovy/org/gradle/api/internal/project/GradleInternalServiceRegistry.java
+++ /dev/null
@@ -1,92 +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.project;
-
-import org.gradle.StartParameter;
-import org.gradle.api.internal.DependencyInjectingInstantiator;
-import org.gradle.api.internal.GradleInternal;
-import org.gradle.api.internal.artifacts.dsl.dependencies.ProjectFinder;
-import org.gradle.api.internal.changedetection.TaskArtifactStateCacheAccess;
-import org.gradle.api.internal.changedetection.TaskCacheLockHandlingBuildExecuter;
-import org.gradle.api.internal.plugins.DefaultPluginRegistry;
-import org.gradle.api.internal.plugins.PluginRegistry;
-import org.gradle.execution.*;
-import org.gradle.execution.taskgraph.DefaultTaskGraphExecuter;
-import org.gradle.execution.taskgraph.TaskPlanExecutor;
-import org.gradle.internal.service.DefaultServiceRegistry;
-import org.gradle.internal.service.ServiceRegistry;
-import org.gradle.listener.ListenerManager;
-
-import java.util.LinkedList;
-import java.util.List;
-
-import static java.util.Arrays.asList;
-
-/**
- * Contains the services for a given {@link GradleInternal} instance.
- */
-public class GradleInternalServiceRegistry extends DefaultServiceRegistry implements ServiceRegistryFactory {
-    private final GradleInternal gradle;
-
-    public GradleInternalServiceRegistry(ServiceRegistry parent, final GradleInternal gradle) {
-        super(parent);
-        this.gradle = gradle;
-        add(new TaskExecutionServices(parent, gradle));
-    }
-
-    protected BuildExecuter createBuildExecuter() {
-        List<BuildConfigurationAction> configs = new LinkedList<BuildConfigurationAction>();
-        if (get(StartParameter.class).isConfigureOnDemand()) {
-            configs.add(new ProjectEvaluatingAction());
-        }
-        configs.add(new DefaultTasksBuildExecutionAction());
-        configs.add(new ExcludedTaskFilteringBuildConfigurationAction());
-        configs.add(new TaskNameResolvingBuildConfigurationAction());
-
-        return new DefaultBuildExecuter(
-                configs,
-                asList(new DryRunBuildExecutionAction(),
-                        new TaskCacheLockHandlingBuildExecuter(get(TaskArtifactStateCacheAccess.class)),
-                        new SelectedTaskExecutionAction()));
-    }
-
-    protected ProjectFinder createProjectFinder() {
-        return new ProjectFinder() {
-            public ProjectInternal getProject(String path) {
-                return gradle.getRootProject().project(path);
-            }
-        };
-    }
-
-    protected IProjectRegistry createIProjectRegistry() {
-        return new DefaultProjectRegistry<ProjectInternal>();
-    }
-
-    protected TaskGraphExecuter createTaskGraphExecuter() {
-        return new DefaultTaskGraphExecuter(get(ListenerManager.class), get(TaskPlanExecutor.class));
-    }
-
-    protected PluginRegistry createPluginRegistry() {
-        return new DefaultPluginRegistry(gradle.getScriptClassLoader(), new DependencyInjectingInstantiator(this));
-    }
-
-    public ServiceRegistryFactory createFor(Object domainObject) {
-        if (domainObject instanceof ProjectInternal) {
-            return new ProjectInternalServiceRegistry(this, (ProjectInternal) domainObject);
-        }
-        throw new UnsupportedOperationException();
-    }
-}
diff --git a/subprojects/core/src/main/groovy/org/gradle/api/internal/project/IProjectFactory.java b/subprojects/core/src/main/groovy/org/gradle/api/internal/project/IProjectFactory.java
index aaa8a91..7a16d04 100644
--- a/subprojects/core/src/main/groovy/org/gradle/api/internal/project/IProjectFactory.java
+++ b/subprojects/core/src/main/groovy/org/gradle/api/internal/project/IProjectFactory.java
@@ -20,8 +20,6 @@ import org.gradle.api.internal.GradleInternal;
 
 /**
  * Creates a {@link ProjectInternal} implementation.
- *
- * @author Hans Dockter
  */
 public interface IProjectFactory {
     ProjectInternal createProject(ProjectDescriptor projectDescriptor, ProjectInternal parent, GradleInternal gradle);
diff --git a/subprojects/core/src/main/groovy/org/gradle/api/internal/project/IProjectRegistry.java b/subprojects/core/src/main/groovy/org/gradle/api/internal/project/IProjectRegistry.java
deleted file mode 100644
index 7136df8..0000000
--- a/subprojects/core/src/main/groovy/org/gradle/api/internal/project/IProjectRegistry.java
+++ /dev/null
@@ -1,40 +0,0 @@
-/*
- * Copyright 2007-2008 the original author or authors.
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- *      http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-package org.gradle.api.internal.project;
-
-import org.gradle.api.specs.Spec;
-
-import java.util.Set;
-import java.io.File;
-
-/**
- * @author Hans Dockter
- */
-public interface IProjectRegistry<T extends ProjectIdentifier> {
-    void addProject(T project);
-
-    T getProject(String path);
-
-    T getProject(File projectDir);
-
-    Set<T> getAllProjects();
-    
-    Set<T> getAllProjects(String path);
-
-    Set<T> getSubProjects(String path);
-
-    Set<T> findAll(Spec<? super T> constraint);
-}
diff --git a/subprojects/core/src/main/groovy/org/gradle/api/internal/project/ProjectFactory.java b/subprojects/core/src/main/groovy/org/gradle/api/internal/project/ProjectFactory.java
index 3fe76da..c3579a9 100644
--- a/subprojects/core/src/main/groovy/org/gradle/api/internal/project/ProjectFactory.java
+++ b/subprojects/core/src/main/groovy/org/gradle/api/internal/project/ProjectFactory.java
@@ -18,16 +18,13 @@ package org.gradle.api.internal.project;
 
 import org.gradle.api.initialization.ProjectDescriptor;
 import org.gradle.api.internal.GradleInternal;
-import org.gradle.internal.reflect.Instantiator;
 import org.gradle.groovy.scripts.ScriptSource;
 import org.gradle.groovy.scripts.StringScriptSource;
 import org.gradle.groovy.scripts.UriScriptSource;
+import org.gradle.internal.reflect.Instantiator;
 
 import java.io.File;
 
-/**
- * @author Hans Dockter
- */
 public class ProjectFactory implements IProjectFactory {
     private final Instantiator instantiator;
 
diff --git a/subprojects/core/src/main/groovy/org/gradle/api/internal/project/ProjectInternal.java b/subprojects/core/src/main/groovy/org/gradle/api/internal/project/ProjectInternal.java
index b5a0fbd..b743906 100644
--- a/subprojects/core/src/main/groovy/org/gradle/api/internal/project/ProjectInternal.java
+++ b/subprojects/core/src/main/groovy/org/gradle/api/internal/project/ProjectInternal.java
@@ -27,9 +27,12 @@ import org.gradle.api.internal.artifacts.configurations.ConfigurationContainerIn
 import org.gradle.api.internal.artifacts.configurations.DependencyMetaDataProvider;
 import org.gradle.api.internal.file.FileOperations;
 import org.gradle.api.internal.file.FileResolver;
+import org.gradle.api.internal.plugins.ExtensionContainerInternal;
 import org.gradle.api.internal.tasks.TaskContainerInternal;
+import org.gradle.configuration.project.ProjectConfigurationActionContainer;
 import org.gradle.groovy.scripts.ScriptAware;
 import org.gradle.groovy.scripts.ScriptSource;
+import org.gradle.internal.service.scopes.ServiceRegistryFactory;
 import org.gradle.logging.StandardOutputCapture;
 
 public interface ProjectInternal extends Project, ProjectIdentifier, ScriptAware, FileOperations, ProcessOperations, DomainObjectContext, DependencyMetaDataProvider {
@@ -53,7 +56,7 @@ public interface ProjectInternal extends Project, ProjectIdentifier, ScriptAware
 
     ProjectInternal findProject(String path);
 
-    IProjectRegistry<ProjectInternal> getProjectRegistry();
+    ProjectRegistry<ProjectInternal> getProjectRegistry();
 
     DynamicObject getInheritedScope();
 
@@ -68,4 +71,8 @@ public interface ProjectInternal extends Project, ProjectIdentifier, ScriptAware
     StandardOutputCapture getStandardOutputCapture();
 
     ProjectStateInternal getState();
+
+    ExtensionContainerInternal getExtensions();
+
+    ProjectConfigurationActionContainer getConfigurationActions();
 }
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
deleted file mode 100644
index 712e621..0000000
--- a/subprojects/core/src/main/groovy/org/gradle/api/internal/project/ProjectInternalServiceRegistry.java
+++ /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.api.internal.project;
-
-import org.gradle.api.AntBuilder;
-import org.gradle.api.artifacts.Module;
-import org.gradle.api.artifacts.dsl.ArtifactHandler;
-import org.gradle.api.artifacts.dsl.DependencyHandler;
-import org.gradle.api.artifacts.dsl.RepositoryHandler;
-import org.gradle.api.component.SoftwareComponentContainer;
-import org.gradle.api.internal.ClassGenerator;
-import org.gradle.api.internal.ClassGeneratorBackedInstantiator;
-import org.gradle.api.internal.DependencyInjectingInstantiator;
-import org.gradle.api.internal.TaskInternal;
-import org.gradle.api.internal.artifacts.ArtifactPublicationServices;
-import org.gradle.api.internal.artifacts.DependencyManagementServices;
-import org.gradle.api.internal.artifacts.DependencyResolutionServices;
-import org.gradle.api.internal.artifacts.ProjectBackedModule;
-import org.gradle.api.internal.artifacts.configurations.ConfigurationContainerInternal;
-import org.gradle.api.internal.artifacts.configurations.DependencyMetaDataProvider;
-import org.gradle.api.internal.artifacts.dsl.dependencies.ProjectFinder;
-import org.gradle.api.internal.component.DefaultSoftwareComponentContainer;
-import org.gradle.api.internal.file.*;
-import org.gradle.api.internal.initialization.DefaultScriptHandlerFactory;
-import org.gradle.api.internal.initialization.ScriptClassLoaderProvider;
-import org.gradle.api.internal.initialization.ScriptHandlerFactory;
-import org.gradle.api.internal.initialization.ScriptHandlerInternal;
-import org.gradle.api.internal.plugins.DefaultProjectsPluginContainer;
-import org.gradle.api.internal.plugins.PluginRegistry;
-import org.gradle.api.internal.project.ant.AntLoggingAdapter;
-import org.gradle.api.internal.project.taskfactory.ITaskFactory;
-import org.gradle.api.internal.tasks.DefaultTaskContainerFactory;
-import org.gradle.api.internal.tasks.TaskContainerInternal;
-import org.gradle.api.plugins.PluginContainer;
-import org.gradle.initialization.ProjectAccessListener;
-import org.gradle.internal.Factory;
-import org.gradle.internal.nativeplatform.filesystem.FileSystem;
-import org.gradle.internal.reflect.Instantiator;
-import org.gradle.internal.service.DefaultServiceRegistry;
-import org.gradle.internal.service.ServiceRegistry;
-import org.gradle.logging.LoggingManagerInternal;
-
-import java.io.File;
-
-/**
- * Contains the services for a given project.
- */
-public class ProjectInternalServiceRegistry extends DefaultServiceRegistry implements ServiceRegistryFactory {
-    private final ProjectInternal project;
-
-    public ProjectInternalServiceRegistry(ServiceRegistry parent, final ProjectInternal project) {
-        super(parent);
-        this.project = project;
-    }
-
-    protected PluginRegistry createPluginRegistry(PluginRegistry parentRegistry) {
-        return parentRegistry.createChild(get(ScriptClassLoaderProvider.class).getClassLoader(), new DependencyInjectingInstantiator(this));
-    }
-
-    protected FileResolver createFileResolver() {
-        return new BaseDirFileResolver(get(FileSystem.class), project.getProjectDir());
-    }
-
-    protected LoggingManagerInternal createLoggingManager() {
-        return getFactory(LoggingManagerInternal.class).create();
-    }
-
-    protected DefaultFileOperations createFileOperations() {
-        return new DefaultFileOperations(get(FileResolver.class), project.getTasks(), get(TemporaryFileProvider.class));
-    }
-
-    protected TemporaryFileProvider createTemporaryFileProvider() {
-        return new DefaultTemporaryFileProvider(new Factory<File>() {
-            public File create() {
-                return new File(project.getBuildDir(), "tmp");
-            }
-        });
-    }
-
-    protected Factory<AntBuilder> createAntBuilderFactory() {
-        return new DefaultAntBuilderFactory(new AntLoggingAdapter(), project);
-    }
-
-    protected PluginContainer createPluginContainer() {
-        return new DefaultProjectsPluginContainer(get(PluginRegistry.class), project);
-    }
-
-    protected ITaskFactory createTaskFactory(ITaskFactory parentFactory) {
-        return parentFactory.createChild(project, new ClassGeneratorBackedInstantiator(get(ClassGenerator.class), new DependencyInjectingInstantiator(this)));
-    }
-
-    protected Factory<TaskContainerInternal> createTaskContainerInternal() {
-        return new DefaultTaskContainerFactory(get(Instantiator.class), get(ITaskFactory.class), project, get(ProjectAccessListener.class));
-    }
-
-    protected ArtifactPublicationServices createArtifactPublicationServices() {
-        return get(DependencyResolutionServices.class).createArtifactPublicationServices();
-    }
-
-    protected RepositoryHandler createRepositoryHandler() {
-        return get(DependencyResolutionServices.class).getResolveRepositoryHandler();
-    }
-
-    protected ConfigurationContainerInternal createConfigurationContainer() {
-        return get(DependencyResolutionServices.class).getConfigurationContainer();
-    }
-
-    protected SoftwareComponentContainer createSoftwareComponentContainer() {
-        Instantiator instantiator = get(Instantiator.class);
-        return instantiator.newInstance(DefaultSoftwareComponentContainer.class, instantiator);
-    }
-
-    protected DependencyResolutionServices createDependencyResolutionServices() {
-        return newDependencyResolutionServices();
-    }
-
-    private DependencyResolutionServices newDependencyResolutionServices() {
-        return get(DependencyManagementServices.class).create(get(FileResolver.class), get(DependencyMetaDataProvider.class), get(ProjectFinder.class), project);
-    }
-
-    protected ArtifactHandler createArtifactHandler() {
-        return get(DependencyResolutionServices.class).getArtifactHandler();
-    }
-
-    protected ProjectFinder createProjectFinder() {
-        return new ProjectFinder() {
-            public ProjectInternal getProject(String path) {
-                return project.project(path);
-            }
-        };
-    }
-
-    protected DependencyHandler createDependencyHandler() {
-        return get(DependencyResolutionServices.class).getDependencyHandler();
-    }
-
-    protected ScriptHandlerInternal createScriptHandler() {
-        ScriptHandlerFactory factory = new DefaultScriptHandlerFactory(
-                get(DependencyManagementServices.class),
-                get(FileResolver.class),
-                get(DependencyMetaDataProvider.class));
-        ClassLoader parentClassLoader;
-        if (project.getParent() != null) {
-            parentClassLoader = project.getParent().getBuildscript().getClassLoader();
-        } else {
-            parentClassLoader = project.getGradle().getScriptClassLoader();
-        }
-        return factory.create(project.getBuildScriptSource(), parentClassLoader, project);
-    }
-
-    protected DependencyMetaDataProvider createDependencyMetaDataProvider() {
-        return new DependencyMetaDataProvider() {
-            public Module getModule() {
-                return new ProjectBackedModule(project);
-            }
-        };
-    }
-
-    public ServiceRegistryFactory createFor(Object domainObject) {
-        if (domainObject instanceof TaskInternal) {
-            return new TaskInternalServiceRegistry(this, project, (TaskInternal)domainObject);
-        }
-        throw new UnsupportedOperationException();
-    }
-
-}
diff --git a/subprojects/core/src/main/groovy/org/gradle/api/internal/project/ProjectRegistry.java b/subprojects/core/src/main/groovy/org/gradle/api/internal/project/ProjectRegistry.java
new file mode 100644
index 0000000..f0daee3
--- /dev/null
+++ b/subprojects/core/src/main/groovy/org/gradle/api/internal/project/ProjectRegistry.java
@@ -0,0 +1,37 @@
+/*
+ * Copyright 2007-2008 the original author or authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.gradle.api.internal.project;
+
+import org.gradle.api.specs.Spec;
+
+import java.io.File;
+import java.util.Set;
+
+public interface ProjectRegistry<T extends ProjectIdentifier> {
+    void addProject(T project);
+
+    T getProject(String path);
+
+    T getProject(File projectDir);
+
+    Set<T> getAllProjects();
+    
+    Set<T> getAllProjects(String path);
+
+    Set<T> getSubProjects(String path);
+
+    Set<T> findAll(Spec<? super T> constraint);
+}
diff --git a/subprojects/core/src/main/groovy/org/gradle/api/internal/project/ProjectStateInternal.java b/subprojects/core/src/main/groovy/org/gradle/api/internal/project/ProjectStateInternal.java
index badaac5..14aa925 100644
--- a/subprojects/core/src/main/groovy/org/gradle/api/internal/project/ProjectStateInternal.java
+++ b/subprojects/core/src/main/groovy/org/gradle/api/internal/project/ProjectStateInternal.java
@@ -46,6 +46,10 @@ public class ProjectStateInternal implements ProjectState {
         this.executing = executing;
     }
 
+    public boolean hasFailure() {
+        return failure != null;
+    }
+
     public Throwable getFailure() {
         return failure;
     }
diff --git a/subprojects/core/src/main/groovy/org/gradle/api/internal/project/ServiceRegistryFactory.java b/subprojects/core/src/main/groovy/org/gradle/api/internal/project/ServiceRegistryFactory.java
deleted file mode 100644
index 1b3e36f..0000000
--- a/subprojects/core/src/main/groovy/org/gradle/api/internal/project/ServiceRegistryFactory.java
+++ /dev/null
@@ -1,31 +0,0 @@
-/*
- * Copyright 2009 the original author or authors.
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- *      http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-package org.gradle.api.internal.project;
-
-import org.gradle.internal.service.ServiceRegistry;
-
-/**
- * A hierarchical service registry.
- */
-public interface ServiceRegistryFactory extends ServiceRegistry {
-    /**
-     * Creates the services for the given domain object.
-     *
-     * @param domainObject The domain object.
-     * @return The registry containing the services for the domain object.
-     */
-    ServiceRegistryFactory createFor(Object domainObject);
-}
diff --git a/subprojects/core/src/main/groovy/org/gradle/api/internal/project/TaskExecutionServices.java b/subprojects/core/src/main/groovy/org/gradle/api/internal/project/TaskExecutionServices.java
deleted file mode 100644
index 146f248..0000000
--- a/subprojects/core/src/main/groovy/org/gradle/api/internal/project/TaskExecutionServices.java
+++ /dev/null
@@ -1,86 +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.api.internal.project;
-
-import org.gradle.StartParameter;
-import org.gradle.api.execution.TaskActionListener;
-import org.gradle.api.internal.changedetection.*;
-import org.gradle.api.internal.tasks.TaskExecuter;
-import org.gradle.api.internal.tasks.execution.*;
-import org.gradle.api.invocation.Gradle;
-import org.gradle.cache.CacheRepository;
-import org.gradle.execution.taskgraph.TaskPlanExecutor;
-import org.gradle.execution.taskgraph.TaskPlanExecutorFactory;
-import org.gradle.internal.id.RandomLongIdGenerator;
-import org.gradle.internal.service.DefaultServiceRegistry;
-import org.gradle.internal.service.ServiceRegistry;
-import org.gradle.listener.ListenerManager;
-
-public class TaskExecutionServices extends DefaultServiceRegistry {
-    private final Gradle gradle;
-
-    public TaskExecutionServices(ServiceRegistry parent, Gradle gradle) {
-        super(parent);
-        this.gradle = gradle;
-    }
-
-    protected TaskExecuter createTaskExecuter() {
-        return new ExecuteAtMostOnceTaskExecuter(
-                new SkipOnlyIfTaskExecuter(
-                        new SkipTaskWithNoActionsExecuter(
-                                new SkipEmptySourceFilesTaskExecuter(
-                                        new ValidatingTaskExecuter(
-                                                new SkipUpToDateTaskExecuter(
-                                                        new CacheLockHandlingTaskExecuter(
-                                                                new PostExecutionAnalysisTaskExecuter(
-                                                                        new ExecuteActionsTaskExecuter(
-                                                                                get(ListenerManager.class).getBroadcaster(TaskActionListener.class))),
-                                                                get(TaskArtifactStateCacheAccess.class)),
-                                                        get(TaskArtifactStateRepository.class)))))));
-    }
-
-    protected TaskArtifactStateCacheAccess createCacheAccess() {
-        return new DefaultTaskArtifactStateCacheAccess(gradle, get(CacheRepository.class));
-    }
-
-    protected TaskArtifactStateRepository createTaskArtifactStateRepository() {
-        TaskArtifactStateCacheAccess cacheAccess = get(TaskArtifactStateCacheAccess.class);
-
-        FileSnapshotter fileSnapshotter = new DefaultFileSnapshotter(
-                new CachingHasher(
-                        new DefaultHasher(),
-                        cacheAccess));
-
-        FileSnapshotter outputFilesSnapshotter = new OutputFilesSnapshotter(fileSnapshotter, new RandomLongIdGenerator(), cacheAccess);
-
-        TaskHistoryRepository taskHistoryRepository = new CacheBackedTaskHistoryRepository(cacheAccess, new CacheBackedFileSnapshotRepository(cacheAccess));
-
-        return new FileCacheBroadcastTaskArtifactStateRepository(
-                new ShortCircuitTaskArtifactStateRepository(
-                        get(StartParameter.class),
-                        new DefaultTaskArtifactStateRepository(
-                                taskHistoryRepository,
-                                fileSnapshotter,
-                                outputFilesSnapshotter)),
-                new DefaultFileCacheListener());
-    }
-
-    protected TaskPlanExecutor createTaskExecutorFactory() {
-        StartParameter startParameter = gradle.getStartParameter();
-        TaskArtifactStateCacheAccess cacheAccess = get(TaskArtifactStateCacheAccess.class);
-        return new TaskPlanExecutorFactory(cacheAccess, startParameter.getParallelThreadCount()).create();
-    }
-}
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
deleted file mode 100644
index 95bbca0..0000000
--- a/subprojects/core/src/main/groovy/org/gradle/api/internal/project/TaskInternalServiceRegistry.java
+++ /dev/null
@@ -1,61 +0,0 @@
-/*
- * Copyright 2010 the original author or authors.
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- *      http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package org.gradle.api.internal.project;
-
-import org.gradle.api.internal.TaskInternal;
-import org.gradle.api.internal.TaskOutputsInternal;
-import org.gradle.api.internal.tasks.DefaultTaskInputs;
-import org.gradle.api.internal.tasks.DefaultTaskOutputs;
-import org.gradle.api.internal.tasks.TaskStatusNagger;
-import org.gradle.api.tasks.TaskInputs;
-import org.gradle.internal.service.DefaultServiceRegistry;
-import org.gradle.internal.service.ServiceRegistry;
-import org.gradle.logging.LoggingManagerInternal;
-
-/**
- * Contains the services for a given task.
- */
-public class TaskInternalServiceRegistry extends DefaultServiceRegistry implements ServiceRegistryFactory {
-    private final ProjectInternal project;
-    private final TaskInternal taskInternal;
-
-    public TaskInternalServiceRegistry(ServiceRegistry parent, final ProjectInternal project, TaskInternal taskInternal) {
-        super(parent);
-        this.project = project;
-        this.taskInternal = taskInternal;
-    }
-
-    protected TaskInputs createTaskInputs() {
-        return new DefaultTaskInputs(project.getFileResolver(), taskInternal, get(TaskStatusNagger.class));
-    }
-
-    protected TaskOutputsInternal createTaskOutputs() {
-        return new DefaultTaskOutputs(project.getFileResolver(), taskInternal, get(TaskStatusNagger.class));
-    }
-
-    protected TaskStatusNagger createTaskStatusNagger() {
-        return new TaskStatusNagger(taskInternal);
-    }
-
-    protected LoggingManagerInternal createLoggingManager() {
-        return getFactory(LoggingManagerInternal.class).create();
-    }
-
-    public ServiceRegistryFactory createFor(Object domainObject) {
-        throw new UnsupportedOperationException();
-    }
-}
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
deleted file mode 100644
index d5b18f4..0000000
--- a/subprojects/core/src/main/groovy/org/gradle/api/internal/project/TopLevelBuildServiceRegistry.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.project;
-
-import org.gradle.StartParameter;
-import org.gradle.api.Project;
-import org.gradle.api.artifacts.Module;
-import org.gradle.api.internal.*;
-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.classpath.ModuleRegistry;
-import org.gradle.api.internal.classpath.PluginModuleRegistry;
-import org.gradle.api.internal.file.FileResolver;
-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.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.cache.CacheRepository;
-import org.gradle.cache.CacheValidator;
-import org.gradle.cache.internal.CacheFactory;
-import org.gradle.cache.internal.DefaultCacheRepository;
-import org.gradle.configuration.*;
-import org.gradle.groovy.scripts.DefaultScriptCompilerFactory;
-import org.gradle.groovy.scripts.ScriptCompilerFactory;
-import org.gradle.groovy.scripts.ScriptExecutionListener;
-import org.gradle.groovy.scripts.internal.*;
-import org.gradle.initialization.*;
-import org.gradle.internal.Factory;
-import org.gradle.internal.TimeProvider;
-import org.gradle.internal.TrueTimeProvider;
-import org.gradle.internal.concurrent.DefaultExecutorFactory;
-import org.gradle.internal.concurrent.ExecutorFactory;
-import org.gradle.internal.id.LongIdGenerator;
-import org.gradle.internal.reflect.Instantiator;
-import org.gradle.internal.service.DefaultServiceRegistry;
-import org.gradle.internal.service.ServiceLocator;
-import org.gradle.internal.service.ServiceRegistry;
-import org.gradle.listener.ListenerManager;
-import org.gradle.logging.LoggingManagerInternal;
-import org.gradle.messaging.actor.ActorFactory;
-import org.gradle.messaging.actor.internal.DefaultActorFactory;
-import org.gradle.messaging.remote.MessagingServer;
-import org.gradle.process.internal.DefaultWorkerProcessFactory;
-import org.gradle.process.internal.WorkerProcessBuilder;
-import org.gradle.process.internal.child.WorkerProcessClassPathProvider;
-import org.gradle.profile.ProfileEventAdapter;
-import org.gradle.profile.ProfileListener;
-import org.gradle.util.ClassLoaderFactory;
-import org.gradle.util.MultiParentClassLoader;
-
-/**
- * Contains the singleton services which are shared by all builds executed by a single {@link org.gradle.GradleLauncher} invocation.
- */
-public class TopLevelBuildServiceRegistry extends DefaultServiceRegistry implements ServiceRegistryFactory {
-    private final StartParameter startParameter;
-
-    public TopLevelBuildServiceRegistry(final ServiceRegistry parent, final StartParameter startParameter) {
-        super(parent);
-        this.startParameter = startParameter;
-        add(StartParameter.class, startParameter);
-    }
-
-    protected ImportsReader createImportsReader() {
-        return new ImportsReader();
-    }
-
-    protected TimeProvider createTimeProvider() {
-        return new TrueTimeProvider();
-    }
-
-    protected ExecutorFactory createExecutorFactory() {
-        return new DefaultExecutorFactory();
-    }
-
-    protected IProjectFactory createProjectFactory() {
-        return new ProjectFactory(get(Instantiator.class));
-    }
-
-    protected ListenerManager createListenerManager(ListenerManager listenerManager) {
-        return listenerManager.createChild();
-    }
-
-    protected ClassPathRegistry createClassPathRegistry() {
-        return new DefaultClassPathRegistry(
-                new DefaultClassPathProvider(get(ModuleRegistry.class)),
-                new DependencyClassPathProvider(get(ModuleRegistry.class), get(PluginModuleRegistry.class)),
-                new WorkerProcessClassPathProvider(get(CacheRepository.class), get(ModuleRegistry.class)));
-    }
-
-    protected IsolatedAntBuilder createIsolatedAntBuilder() {
-        return new DefaultIsolatedAntBuilder(get(ClassPathRegistry.class), get(ClassLoaderFactory.class));
-    }
-
-    protected ActorFactory createActorFactory() {
-        return new DefaultActorFactory(get(ExecutorFactory.class));
-    }
-
-    protected IGradlePropertiesLoader createGradlePropertiesLoader() {
-        return new DefaultGradlePropertiesLoader(startParameter);
-    }
-
-    protected BuildLoader createBuildLoader() {
-        return new ProjectPropertySettingBuildLoader(
-                get(IGradlePropertiesLoader.class),
-                new InstantiatingBuildLoader(get(IProjectFactory.class)));
-    }
-
-    protected CacheFactory createCacheFactory() {
-        return getFactory(CacheFactory.class).create();
-    }
-
-    protected CacheRepository createCacheRepository() {
-        CacheFactory factory = get(CacheFactory.class);
-        return new DefaultCacheRepository(startParameter.getGradleUserHomeDir(), startParameter.getProjectCacheDir(),
-                startParameter.getCacheUsage(), factory);
-    }
-
-    protected ProjectEvaluator createProjectEvaluator() {
-        return new LifecycleProjectEvaluator(
-                new BuildScriptProcessor(
-                        get(ScriptPluginFactory.class)));
-    }
-
-    protected ITaskFactory createITaskFactory() {
-        return new DependencyAutoWireTaskFactory(
-                new AnnotationProcessingTaskFactory(
-                        new TaskFactory(
-                                get(ClassGenerator.class))));
-    }
-
-    protected ScriptCompilerFactory createScriptCompileFactory() {
-        ScriptExecutionListener scriptExecutionListener = get(ListenerManager.class).getBroadcaster(ScriptExecutionListener.class);
-        EmptyScriptGenerator emptyScriptGenerator = new AsmBackedEmptyScriptGenerator();
-        CacheValidator scriptCacheInvalidator = new CacheValidator() {
-            public boolean isValid() {
-                return !get(StartParameter.class).isRecompileScripts();
-            }
-        };
-        return new DefaultScriptCompilerFactory(
-                new CachingScriptClassCompiler(
-                        new ShortCircuitEmptyScriptCompiler(
-                                new FileCacheBackedScriptClassCompiler(
-                                        get(CacheRepository.class),
-                                        scriptCacheInvalidator,
-                                        new DefaultScriptCompilationHandler(
-                                                emptyScriptGenerator)),
-                                emptyScriptGenerator)),
-                new DefaultScriptRunnerFactory(scriptExecutionListener));
-    }
-
-    protected ScriptPluginFactory createScriptObjectConfigurerFactory() {
-        return new DefaultScriptPluginFactory(
-                get(ScriptCompilerFactory.class),
-                get(ImportsReader.class),
-                get(ScriptHandlerFactory.class),
-                get(ClassLoader.class),
-                getFactory(LoggingManagerInternal.class));
-    }
-
-    protected MultiParentClassLoader createRootClassLoader() {
-        return get(ClassLoaderRegistry.class).createScriptClassLoader();
-    }
-
-    protected InitScriptHandler createInitScriptHandler() {
-        return new InitScriptHandler(
-                new DefaultInitScriptProcessor(get(ScriptPluginFactory.class))
-        );
-    }
-
-    protected SettingsProcessor createSettingsProcessor() {
-        return new PropertiesLoadingSettingsProcessor(
-                new ScriptEvaluatingSettingsProcessor(
-                        get(ScriptPluginFactory.class),
-                        new SettingsFactory(
-                                new DefaultProjectDescriptorRegistry(),
-                                get(Instantiator.class)
-                        ),
-                        get(IGradlePropertiesLoader.class)),
-                get(IGradlePropertiesLoader.class));
-    }
-
-    protected ExceptionAnalyser createExceptionAnalyser() {
-        return new MultipleBuildFailuresExceptionAnalyser(new DefaultExceptionAnalyser(get(ListenerManager.class)));
-    }
-
-    protected ScriptHandlerFactory createScriptHandlerFactory() {
-        return new DefaultScriptHandlerFactory(
-                get(DependencyManagementServices.class),
-                get(FileResolver.class),
-                new DependencyMetaDataProviderImpl());
-    }
-
-    protected FileResolver createFileResolver() {
-        return new IdentityFileResolver();
-    }
-
-    protected Factory<WorkerProcessBuilder> createWorkerProcessFactory() {
-        ClassPathRegistry classPathRegistry = get(ClassPathRegistry.class);
-        return new DefaultWorkerProcessFactory(startParameter.getLogLevel(), get(MessagingServer.class), classPathRegistry,
-                new IdentityFileResolver(), new LongIdGenerator());
-    }
-
-    protected BuildConfigurer createBuildConfigurer() {
-        return new DefaultBuildConfigurer();
-    }
-
-    protected ProjectAccessListener createProjectAccessListener() {
-        return new DefaultProjectAccessListener();
-    }
-
-    protected ProfileEventAdapter createProfileEventAdapter() {
-        return new ProfileEventAdapter(get(BuildRequestMetaData.class), get(TimeProvider.class), get(ListenerManager.class).getBroadcaster(ProfileListener.class));
-    }
-
-    protected DependencyManagementServices createDependencyManagementServices() {
-        ClassLoader coreImplClassLoader = get(ClassLoaderRegistry.class).getCoreImplClassLoader();
-        ServiceLocator serviceLocator = new ServiceLocator(coreImplClassLoader);
-        return serviceLocator.getFactory(DependencyManagementServices.class).newInstance(this);
-    }
-
-    public ServiceRegistryFactory createFor(Object domainObject) {
-        if (domainObject instanceof GradleInternal) {
-            return new GradleInternalServiceRegistry(this, (GradleInternal) domainObject);
-        }
-        throw new IllegalArgumentException(String.format("Cannot create services for unknown domain object of type %s.",
-                domainObject.getClass().getSimpleName()));
-    }
-
-    private class DependencyMetaDataProviderImpl implements DependencyMetaDataProvider {
-
-        public Module getModule() {
-            return new DefaultModule("unspecified", "unspecified", Project.DEFAULT_VERSION, Project.DEFAULT_STATUS);
-        }
-    }
-}
diff --git a/subprojects/core/src/main/groovy/org/gradle/api/internal/project/ant/AntLoggingAdapter.java b/subprojects/core/src/main/groovy/org/gradle/api/internal/project/ant/AntLoggingAdapter.java
index ada6e6d..fe274da 100644
--- a/subprojects/core/src/main/groovy/org/gradle/api/internal/project/ant/AntLoggingAdapter.java
+++ b/subprojects/core/src/main/groovy/org/gradle/api/internal/project/ant/AntLoggingAdapter.java
@@ -23,9 +23,6 @@ import org.gradle.api.logging.Logging;
 
 import java.io.PrintStream;
 
-/**
- * @author Hans Dockter
- */
 public class AntLoggingAdapter implements BuildLogger {
     private final Logger logger = Logging.getLogger(AntLoggingAdapter.class);
 
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 eedcb7d..28059ef 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
@@ -16,16 +16,22 @@
 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.Task;
-import org.gradle.api.Transformer;
+import org.gradle.api.*;
+import org.gradle.api.internal.AbstractTask;
+import org.gradle.api.internal.ConventionTask;
 import org.gradle.api.internal.TaskInternal;
+import org.gradle.api.internal.changedetection.TaskArtifactState;
 import org.gradle.api.internal.project.ProjectInternal;
+import org.gradle.api.internal.tasks.ContextAwareTaskAction;
+import org.gradle.api.internal.tasks.TaskExecutionContext;
 import org.gradle.api.internal.tasks.execution.TaskValidator;
+import org.gradle.api.specs.Spec;
 import org.gradle.api.tasks.*;
+import org.gradle.api.tasks.incremental.IncrementalTaskInputs;
+import org.gradle.internal.Factory;
 import org.gradle.internal.reflect.Instantiator;
-import org.gradle.util.ReflectionUtil;
+import org.gradle.internal.reflect.JavaReflectionUtil;
+import org.gradle.util.DeprecationLogger;
 
 import java.io.File;
 import java.lang.annotation.Annotation;
@@ -41,8 +47,8 @@ import java.util.concurrent.Callable;
  */
 public class AnnotationProcessingTaskFactory implements ITaskFactory {
     private final ITaskFactory taskFactory;
-    private final Map<Class, List<Action<Task>>> actionsForType;
-    
+    private final Map<Class, TaskClassInfo> classInfos;
+
     private final Transformer<Iterable<File>, Object> filePropertyTransformer = new Transformer<Iterable<File>, Object>() {
         public Iterable<File> transform(Object original) {
             File file = (File) original;
@@ -56,7 +62,7 @@ public class AnnotationProcessingTaskFactory implements ITaskFactory {
             return original != null ? (Iterable<File>) original : Collections.<File>emptyList();
         }
     };
-    
+
     private final List<? extends PropertyAnnotationHandler> handlers = Arrays.asList(
             new InputFilePropertyAnnotationHandler(),
             new InputDirectoryPropertyAnnotationHandler(),
@@ -77,66 +83,71 @@ public class AnnotationProcessingTaskFactory implements ITaskFactory {
 
     public AnnotationProcessingTaskFactory(ITaskFactory taskFactory) {
         this.taskFactory = taskFactory;
-        this.actionsForType = new HashMap<Class, List<Action<Task>>>();
+        this.classInfos = new HashMap<Class, TaskClassInfo>();
     }
 
-    private AnnotationProcessingTaskFactory(Map<Class, List<Action<Task>>> actionsForType, ITaskFactory taskFactory) {
-        this.actionsForType = actionsForType;
+    private AnnotationProcessingTaskFactory(Map<Class, TaskClassInfo> classInfos, ITaskFactory taskFactory) {
+        this.classInfos = classInfos;
         this.taskFactory = taskFactory;
     }
 
     public ITaskFactory createChild(ProjectInternal project, Instantiator instantiator) {
-        return new AnnotationProcessingTaskFactory(actionsForType, taskFactory.createChild(project, instantiator));
+        return new AnnotationProcessingTaskFactory(classInfos, taskFactory.createChild(project, instantiator));
     }
 
     public TaskInternal createTask(Map<String, ?> args) {
         TaskInternal task = taskFactory.createTask(args);
+        TaskClassInfo taskClassInfo = getTaskClassInfo(task.getClass());
+
+        // TODO:DAZ Make this more general purpose, and support IncrementalTaskActions added via another mechanism.
+        if (taskClassInfo.incremental) {
+            // Add a dummy upToDateWhen spec: this will for TaskOutputs.hasOutputs() to be true.
+            task.getOutputs().upToDateWhen(new Spec<Task>() {
+                public boolean isSatisfiedBy(Task element) {
+                    return true;
+                }
+            });
+        }
 
-        Class<? extends Task> type = task.getClass();
-        List<Action<Task>> actions = actionsForType.get(type);
-        if (actions == null) {
-            actions = createActionsForType(type);
-            actionsForType.put(type, actions);
+        for (Factory<Action<Task>> actionFactory : taskClassInfo.taskActions) {
+            task.doFirst(actionFactory.create());
         }
 
-        for (Action<Task> action : actions) {
-            task.doFirst(action);
-            if (action instanceof Validator) {
-                Validator validator = (Validator) action;
-                validator.addInputsAndOutputs(task);
-            }
+        if (taskClassInfo.validator != null) {
+            task.doFirst(taskClassInfo.validator);
+            taskClassInfo.validator.addInputsAndOutputs(task);
         }
 
         return task;
     }
 
-    private List<Action<Task>> createActionsForType(Class<? extends Task> type) {
-        List<Action<Task>> actions = new ArrayList<Action<Task>>();
-        findTaskActions(type, actions);
-        findProperties(type, actions);
-        return actions;
-    }
-
-    private void findProperties(Class<? extends Task> type, List<Action<Task>> actions) {
-        Validator validator = new Validator();
+    private TaskClassInfo getTaskClassInfo(Class<? extends Task> type) {
+        TaskClassInfo taskClassInfo = classInfos.get(type);
+        if (taskClassInfo == null) {
+            taskClassInfo = new TaskClassInfo();
+            findTaskActions(type, taskClassInfo);
 
-        validator.attachActions(null, type);
+            Validator validator = new Validator();
+            validator.attachActions(null, type);
 
-        if (!validator.properties.isEmpty()) {
-            actions.add(validator);
+            if (!validator.properties.isEmpty()) {
+                taskClassInfo.validator = validator;
+            }
+            classInfos.put(type, taskClassInfo);
         }
+        return taskClassInfo;
     }
 
-    private void findTaskActions(Class<? extends Task> type, List<Action<Task>> actions) {
+    private void findTaskActions(Class<? extends Task> type, TaskClassInfo taskClassInfo) {
         Set<String> methods = new HashSet<String>();
         for (Class current = type; current != null; current = current.getSuperclass()) {
             for (Method method : current.getDeclaredMethods()) {
-                attachTaskAction(method, actions, methods);
+                attachTaskAction(method, taskClassInfo, methods);
             }
         }
     }
 
-    private void attachTaskAction(final Method method, Collection<Action<Task>> actions, Collection<String> methods) {
+    private void attachTaskAction(final Method method, TaskClassInfo taskClassInfo, Collection<String> processedMethods) {
         if (method.getAnnotation(TaskAction.class) == null) {
             return;
         }
@@ -144,33 +155,95 @@ public class AnnotationProcessingTaskFactory implements ITaskFactory {
             throw new GradleException(String.format("Cannot use @TaskAction annotation on static method %s.%s().",
                     method.getDeclaringClass().getSimpleName(), method.getName()));
         }
-        if (method.getParameterTypes().length > 0) {
+        final Class<?>[] parameterTypes = method.getParameterTypes();
+        if (parameterTypes.length > 1) {
             throw new GradleException(String.format(
-                    "Cannot use @TaskAction annotation on method %s.%s() as this method takes parameters.",
+                    "Cannot use @TaskAction annotation on method %s.%s() as this method takes multiple parameters.",
                     method.getDeclaringClass().getSimpleName(), method.getName()));
         }
-        if (methods.contains(method.getName())) {
+
+        if (parameterTypes.length == 1) {
+            if (!parameterTypes[0].equals(IncrementalTaskInputs.class)) {
+                throw new GradleException(String.format(
+                        "Cannot use @TaskAction annotation on method %s.%s() because %s is not a valid parameter to an action method.",
+                        method.getDeclaringClass().getSimpleName(), method.getName(), parameterTypes[0]));
+            }
+            if (taskClassInfo.incremental) {
+                throw new GradleException(String.format("Cannot have multiple @TaskAction methods accepting an %s parameter.", IncrementalTaskInputs.class.getSimpleName()));
+            }
+            taskClassInfo.incremental = true;
+        }
+        if (processedMethods.contains(method.getName())) {
             return;
         }
-        methods.add(method.getName());
-        actions.add(new Action<Task>() {
-            public void execute(Task task) {
-                ClassLoader original = Thread.currentThread().getContextClassLoader();
-                Thread.currentThread().setContextClassLoader(method.getDeclaringClass().getClassLoader());
-                try {
-                    ReflectionUtil.invoke(task, method.getName());
-                } finally {
-                    Thread.currentThread().setContextClassLoader(original);
+        taskClassInfo.taskActions.add(createActionFactory(method, parameterTypes));
+        processedMethods.add(method.getName());
+    }
+
+    private Factory<Action<Task>> createActionFactory(final Method method, final Class<?>[] parameterTypes) {
+        return new Factory<Action<Task>>() {
+            public Action<Task> create() {
+                if (parameterTypes.length == 1) {
+                    return new IncrementalTaskAction(method);
+                } else {
+                    return new StandardTaskAction(method);
                 }
             }
-        });
+        };
     }
 
     private static boolean isGetter(Method method) {
-        return method.getName().startsWith("get") && method.getReturnType() != Void.TYPE
+        return ((method.getName().startsWith("get") && method.getReturnType() != Void.TYPE)
+                || (method.getName().startsWith("is") && method.getReturnType().equals(boolean.class)))
                 && method.getParameterTypes().length == 0 && !Modifier.isStatic(method.getModifiers());
     }
 
+    private static class StandardTaskAction implements Action<Task> {
+        private final Method method;
+
+        public StandardTaskAction(Method method) {
+            this.method = method;
+        }
+
+        public void execute(Task task) {
+            ClassLoader original = Thread.currentThread().getContextClassLoader();
+            Thread.currentThread().setContextClassLoader(method.getDeclaringClass().getClassLoader());
+            try {
+                doExecute(task, method.getName());
+            } finally {
+                Thread.currentThread().setContextClassLoader(original);
+            }
+        }
+
+        protected void doExecute(Task task, String methodName) {
+            JavaReflectionUtil.method(task, Object.class, methodName).invoke(task);
+        }
+    }
+
+    public static class IncrementalTaskAction extends StandardTaskAction implements ContextAwareTaskAction {
+
+        private TaskArtifactState taskArtifactState;
+
+        public IncrementalTaskAction(Method method) {
+            super(method);
+        }
+
+        public void contextualise(TaskExecutionContext context) {
+            this.taskArtifactState = context == null ? null : context.getTaskArtifactState();
+        }
+
+        protected void doExecute(Task task, String methodName) {
+            JavaReflectionUtil.method(task, Object.class, methodName, IncrementalTaskInputs.class).invoke(task, taskArtifactState.getInputChanges());
+            taskArtifactState = null;
+        }
+    }
+
+    private static class TaskClassInfo {
+        public Validator validator;
+        public List<Factory<Action<Task>>> taskActions = new ArrayList<Factory<Action<Task>>>();
+        public boolean incremental;
+    }
+
     private class Validator implements Action<Task>, TaskValidator {
         private Set<PropertyInfo> properties = new LinkedHashSet<PropertyInfo>();
 
@@ -204,15 +277,23 @@ public class AnnotationProcessingTaskFactory implements ITaskFactory {
         }
 
         public void attachActions(PropertyInfo parent, Class<?> type) {
-            if (type.getSuperclass() != null) {
-                attachActions(parent, type.getSuperclass());
+            Class<?> superclass = type.getSuperclass();
+            if (!(superclass == null
+                    // Avoid reflecting on classes we know we don't need to look at
+                    || superclass.equals(ConventionTask.class) || superclass.equals(DefaultTask.class)
+                    || superclass.equals(AbstractTask.class) || superclass.equals(Object.class)
+            )) {
+                attachActions(parent, superclass);
             }
+
             for (Method method : type.getDeclaredMethods()) {
                 if (!isGetter(method)) {
                     continue;
                 }
 
-                String fieldName = StringUtils.uncapitalize(method.getName().substring(3));
+                String name = method.getName();
+                int prefixLength = name.startsWith("is") ? 2 : 3; // it's 'get' if not 'is'.
+                String fieldName = StringUtils.uncapitalize(name.substring(prefixLength));
                 String propertyName = fieldName;
                 if (parent != null) {
                     propertyName = parent.getName() + '.' + propertyName;
@@ -307,7 +388,7 @@ public class AnnotationProcessingTaskFactory implements ITaskFactory {
             this.validator = validator;
             this.parent = parent;
             this.propertyName = propertyName;
-            this.method = method;   
+            this.method = method;
         }
 
         @Override
@@ -366,7 +447,12 @@ public class AnnotationProcessingTaskFactory implements ITaskFactory {
                 bean = parentValue.getValue();
             }
 
-            final Object value = ReflectionUtil.invoke(bean, method.getName());
+            final Object finalBean = bean;
+            final Object value = DeprecationLogger.whileDisabled(new Factory<Object>() {
+                public Object create() {
+                    return JavaReflectionUtil.method(finalBean, Object.class, method).invoke(finalBean);
+                }
+            });
 
             return new PropertyValue() {
                 public Object getValue() {
diff --git a/subprojects/core/src/main/groovy/org/gradle/api/internal/project/taskfactory/ITaskFactory.java b/subprojects/core/src/main/groovy/org/gradle/api/internal/project/taskfactory/ITaskFactory.java
index f2dc175..c18d79a 100644
--- a/subprojects/core/src/main/groovy/org/gradle/api/internal/project/taskfactory/ITaskFactory.java
+++ b/subprojects/core/src/main/groovy/org/gradle/api/internal/project/taskfactory/ITaskFactory.java
@@ -21,9 +21,6 @@ import org.gradle.internal.reflect.Instantiator;
 
 import java.util.Map;
 
-/**
- * @author Hans Dockter
- */
 public interface ITaskFactory {
     public ITaskFactory createChild(ProjectInternal project, Instantiator instantiator);
 
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 16874d6..e3fef78 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
@@ -30,17 +30,17 @@ import org.gradle.internal.reflect.ObjectInstantiationException;
 import org.gradle.util.GUtil;
 
 import java.util.HashMap;
+import java.util.HashSet;
 import java.util.Map;
+import java.util.Set;
 import java.util.concurrent.Callable;
 
-/**
- * @author Hans Dockter
- */
 public class TaskFactory implements ITaskFactory {
     public static final String GENERATE_SUBCLASS = "generateSubclass";
     private final ClassGenerator generator;
     private final ProjectInternal project;
     private final Instantiator instantiator;
+    private final Set<String> validTaskArguments;
 
     public TaskFactory(ClassGenerator generator) {
         this(generator, null, null);
@@ -50,6 +50,17 @@ public class TaskFactory implements ITaskFactory {
         this.generator = generator;
         this.project = project;
         this.instantiator = instantiator;
+
+
+        validTaskArguments = new HashSet<String>();
+        validTaskArguments.add(Task.TASK_ACTION);
+        validTaskArguments.add(Task.TASK_DEPENDS_ON);
+        validTaskArguments.add(Task.TASK_DESCRIPTION);
+        validTaskArguments.add(Task.TASK_GROUP);
+        validTaskArguments.add(Task.TASK_NAME);
+        validTaskArguments.add(Task.TASK_OVERWRITE);
+        validTaskArguments.add(Task.TASK_TYPE);
+
     }
 
     public ITaskFactory createChild(ProjectInternal project, Instantiator instantiator) {
@@ -120,6 +131,7 @@ public class TaskFactory implements ITaskFactory {
     }
 
     private void checkTaskArgsAndCreateDefaultValues(Map<String, Object> args) {
+        validateArgs(args);
         setIfNull(args, Task.TASK_NAME, "");
         setIfNull(args, Task.TASK_TYPE, DefaultTask.class);
         if (((Class) args.get(Task.TASK_TYPE)).isAssignableFrom(DefaultTask.class)) {
@@ -128,6 +140,15 @@ public class TaskFactory implements ITaskFactory {
         setIfNull(args, GENERATE_SUBCLASS, "true");
     }
 
+    private void validateArgs(Map<String, Object> args) {
+        if (!validTaskArguments.containsAll(args.keySet())) {
+            Map unknownArguments = new HashMap<String, Object>(args);
+            unknownArguments.keySet().removeAll(validTaskArguments);
+            throw new InvalidUserDataException(String.format("Could not create task '%s': Unknown argument(s) in task definition: %s",
+                        args.get(Task.TASK_NAME), unknownArguments.keySet()));
+        }
+    }
+
     private void setIfNull(Map<String, Object> map, String key, Object defaultValue) {
         if (map.get(key) == null) {
             map.put(key, defaultValue);
diff --git a/subprojects/core/src/main/groovy/org/gradle/api/internal/resources/DefaultResourceHandler.java b/subprojects/core/src/main/groovy/org/gradle/api/internal/resources/DefaultResourceHandler.java
index 453c454..450cf92 100644
--- a/subprojects/core/src/main/groovy/org/gradle/api/internal/resources/DefaultResourceHandler.java
+++ b/subprojects/core/src/main/groovy/org/gradle/api/internal/resources/DefaultResourceHandler.java
@@ -23,9 +23,6 @@ import org.gradle.api.internal.file.archive.compression.GzipArchiver;
 import org.gradle.api.resources.ReadableResource;
 import org.gradle.api.resources.ResourceHandler;
 
-/**
- * by Szczepan Faber, created at: 11/24/11
- */
 public class DefaultResourceHandler implements ResourceHandler {
     private final FileResolver resolver;
 
diff --git a/subprojects/core/src/main/groovy/org/gradle/api/internal/resources/URIBuilder.java b/subprojects/core/src/main/groovy/org/gradle/api/internal/resources/URIBuilder.java
index 288ca63..2059269 100644
--- a/subprojects/core/src/main/groovy/org/gradle/api/internal/resources/URIBuilder.java
+++ b/subprojects/core/src/main/groovy/org/gradle/api/internal/resources/URIBuilder.java
@@ -20,9 +20,6 @@ import org.gradle.util.GUtil;
 
 import java.net.URI;
 
-/**
- * by Szczepan Faber, created at: 12/13/11
- */
 public class URIBuilder {
     private final URI uri;
     private String schemePrefix = "";
diff --git a/subprojects/core/src/main/groovy/org/gradle/api/internal/specs/ExplainingSpecs.java b/subprojects/core/src/main/groovy/org/gradle/api/internal/specs/ExplainingSpecs.java
index 8747618..e4a03d0 100644
--- a/subprojects/core/src/main/groovy/org/gradle/api/internal/specs/ExplainingSpecs.java
+++ b/subprojects/core/src/main/groovy/org/gradle/api/internal/specs/ExplainingSpecs.java
@@ -16,9 +16,6 @@
 
 package org.gradle.api.internal.specs;
 
-/**
- * by Szczepan Faber, created at: 5/14/12
- */
 public class ExplainingSpecs {
 
     private static final ExplainingSpec<Object> SATISFIES_ALL = new ExplainingSpec<Object>() {
diff --git a/subprojects/core/src/main/groovy/org/gradle/api/internal/tasks/CachingTaskDependencyResolveContext.java b/subprojects/core/src/main/groovy/org/gradle/api/internal/tasks/CachingTaskDependencyResolveContext.java
index e235a3a..c993c4f 100644
--- a/subprojects/core/src/main/groovy/org/gradle/api/internal/tasks/CachingTaskDependencyResolveContext.java
+++ b/subprojects/core/src/main/groovy/org/gradle/api/internal/tasks/CachingTaskDependencyResolveContext.java
@@ -19,8 +19,8 @@ package org.gradle.api.internal.tasks;
 import org.gradle.api.Buildable;
 import org.gradle.api.GradleException;
 import org.gradle.api.Task;
-import org.gradle.api.internal.CachingDirectedGraphWalker;
-import org.gradle.api.internal.DirectedGraph;
+import org.gradle.internal.graph.CachingDirectedGraphWalker;
+import org.gradle.internal.graph.DirectedGraph;
 import org.gradle.api.tasks.TaskDependency;
 
 import java.util.*;
@@ -79,7 +79,7 @@ public class CachingTaskDependencyResolveContext implements TaskDependencyResolv
     }
 
     private class TaskGraphImpl implements DirectedGraph<Object, Task> {
-        public void getNodeValues(Object node, Collection<Task> values, Collection<Object> connectedNodes) {
+        public void getNodeValues(Object node, Collection<? super Task> values, Collection<? super Object> connectedNodes) {
             if (node instanceof TaskDependencyInternal) {
                 TaskDependencyInternal taskDependency = (TaskDependencyInternal) node;
                 queue.clear();
diff --git a/subprojects/core/src/main/groovy/org/gradle/api/internal/tasks/ContextAwareTaskAction.java b/subprojects/core/src/main/groovy/org/gradle/api/internal/tasks/ContextAwareTaskAction.java
new file mode 100644
index 0000000..1b77f0c
--- /dev/null
+++ b/subprojects/core/src/main/groovy/org/gradle/api/internal/tasks/ContextAwareTaskAction.java
@@ -0,0 +1,24 @@
+/*
+ * Copyright 2013 the original author or authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY 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;
+
+public interface ContextAwareTaskAction extends Action<Task> {
+    void contextualise(TaskExecutionContext context);
+}
diff --git a/subprojects/core/src/main/groovy/org/gradle/api/internal/tasks/DefaultTaskContainer.java b/subprojects/core/src/main/groovy/org/gradle/api/internal/tasks/DefaultTaskContainer.java
index eb4cbde..4ea86f2 100644
--- a/subprojects/core/src/main/groovy/org/gradle/api/internal/tasks/DefaultTaskContainer.java
+++ b/subprojects/core/src/main/groovy/org/gradle/api/internal/tasks/DefaultTaskContainer.java
@@ -17,29 +17,25 @@ package org.gradle.api.internal.tasks;
 
 import groovy.lang.Closure;
 import org.apache.commons.lang.StringUtils;
-import org.gradle.api.InvalidUserDataException;
-import org.gradle.api.Project;
-import org.gradle.api.Task;
-import org.gradle.api.UnknownTaskException;
-import org.gradle.api.internal.CachingDirectedGraphWalker;
-import org.gradle.api.internal.DirectedGraph;
+import org.gradle.api.*;
 import org.gradle.api.internal.DynamicObject;
 import org.gradle.api.internal.NamedDomainObjectContainerConfigureDelegate;
 import org.gradle.api.internal.project.ProjectInternal;
 import org.gradle.api.internal.project.taskfactory.ITaskFactory;
 import org.gradle.initialization.ProjectAccessListener;
+import org.gradle.internal.graph.CachingDirectedGraphWalker;
+import org.gradle.internal.graph.DirectedGraph;
 import org.gradle.internal.reflect.Instantiator;
 import org.gradle.util.ConfigureUtil;
 import org.gradle.util.DeprecationLogger;
 import org.gradle.util.GUtil;
 
-import java.util.Collection;
-import java.util.HashMap;
-import java.util.Map;
+import java.util.*;
 
 public class DefaultTaskContainer extends DefaultTaskCollection<Task> implements TaskContainerInternal {
     private final ITaskFactory taskFactory;
     private final ProjectAccessListener projectAccessListener;
+    private Map<String, Runnable> placeholders = new HashMap<String, Runnable>();
 
     public DefaultTaskContainer(ProjectInternal project, Instantiator instantiator, ITaskFactory taskFactory, ProjectAccessListener projectAccessListener) {
         super(Task.class, instantiator, project);
@@ -47,7 +43,7 @@ public class DefaultTaskContainer extends DefaultTaskCollection<Task> implements
         this.projectAccessListener = projectAccessListener;
     }
 
-    public Task add(Map<String, ?> options) {
+    public Task create(Map<String, ?> options) {
         Map<String, Object> mutableOptions = new HashMap<String, Object>(options);
 
         Object replaceStr = mutableOptions.remove(Task.TASK_OVERWRITE);
@@ -71,16 +67,37 @@ public class DefaultTaskContainer extends DefaultTaskCollection<Task> implements
         return task;
     }
 
+    public Task add(Map<String, ?> options) {
+        DeprecationLogger.nagUserOfReplacedMethod("TaskContainer.add()", "create()");
+        return create(options);
+    }
+
+    public Task create(Map<String, ?> options, Closure configureClosure) throws InvalidUserDataException {
+        return create(options).configure(configureClosure);
+    }
+
     public Task add(Map<String, ?> options, Closure configureClosure) throws InvalidUserDataException {
-        return add(options).configure(configureClosure);
+        DeprecationLogger.nagUserOfReplacedMethod("TaskContainer.add()", "create()");
+        return create(options, configureClosure);
+    }
+
+    public <T extends Task> T create(String name, Class<T> type) {
+        return type.cast(create(GUtil.map(Task.TASK_NAME, name, Task.TASK_TYPE, type)));
     }
 
     public <T extends Task> T add(String name, Class<T> type) {
-        return type.cast(add(GUtil.map(Task.TASK_NAME, name, Task.TASK_TYPE, type)));
+        DeprecationLogger.nagUserOfReplacedMethod("TaskContainer.add()", "create()");
+        return create(name, type);
     }
 
     public Task create(String name) {
-        return add(name);
+        return create(GUtil.map(Task.TASK_NAME, name));
+    }
+
+    public Task create(String name, Action<? super Task> configureAction) throws InvalidUserDataException {
+        Task task = create(name);
+        configureAction.execute(task);
+        return task;
     }
 
     public Task maybeCreate(String name) {
@@ -92,23 +109,31 @@ public class DefaultTaskContainer extends DefaultTaskCollection<Task> implements
     }
 
     public Task add(String name) {
-        return add(GUtil.map(Task.TASK_NAME, name));
+        DeprecationLogger.nagUserOfReplacedMethod("TaskContainer.add()", "create()");
+        return create(name);
     }
 
     public Task replace(String name) {
-        return add(GUtil.map(Task.TASK_NAME, name, Task.TASK_OVERWRITE, true));
+        return create(GUtil.map(Task.TASK_NAME, name, Task.TASK_OVERWRITE, true));
     }
 
     public Task create(String name, Closure configureClosure) {
-        return add(name, configureClosure);
+        return create(name).configure(configureClosure);
     }
 
     public Task add(String name, Closure configureClosure) {
-        return add(GUtil.map(Task.TASK_NAME, name)).configure(configureClosure);
+        DeprecationLogger.nagUserOfReplacedMethod("TaskContainer.add()", "create()");
+        return create(name, configureClosure);
+    }
+
+    public <T extends Task> T create(String name, Class<T> type, Action<? super T> configuration) throws InvalidUserDataException {
+        T task = create(name, type);
+        configuration.execute(task);
+        return task;
     }
 
     public <T extends Task> T replace(String name, Class<T> type) {
-        return type.cast(add(GUtil.map(Task.TASK_NAME, name, Task.TASK_TYPE, type, Task.TASK_OVERWRITE, true)));
+        return type.cast(create(GUtil.map(Task.TASK_NAME, name, Task.TASK_TYPE, type, Task.TASK_OVERWRITE, true)));
     }
 
     public Task findByPath(String path) {
@@ -133,7 +158,7 @@ public class DefaultTaskContainer extends DefaultTaskCollection<Task> implements
         if (!GUtil.isTrue(path)) {
             throw new InvalidUserDataException("A path must be specified!");
         }
-        if(!(path instanceof CharSequence)) {
+        if (!(path instanceof CharSequence)) {
             DeprecationLogger.nagUserOfDeprecated(
                     String.format("Converting class %s to a task dependency using toString()", path.getClass().getName()),
                     "Please use org.gradle.api.Task, java.lang.String, org.gradle.api.Buildable, org.gradle.tasks.TaskDependency or a Closure to declare your task dependencies"
@@ -163,11 +188,55 @@ public class DefaultTaskContainer extends DefaultTaskCollection<Task> implements
         return getElementsAsDynamicObject();
     }
 
+    public SortedSet<String> getNames() {
+        SortedSet<String> set = new TreeSet<String>();
+        for (Task o : getStore()) {
+            set.add(o.getName());
+        }
+        for (String placeHolderName : placeholders.keySet()) {
+            set.add(placeHolderName);
+        }
+        return set;
+    }
+
     public void actualize() {
         new CachingDirectedGraphWalker<Task, Void>(new DirectedGraph<Task, Void>() {
-            public void getNodeValues(Task node, Collection<Void> values, Collection<Task> connectedNodes) {
+            public void getNodeValues(Task node, Collection<? super Void> values, Collection<? super Task> connectedNodes) {
                 connectedNodes.addAll(node.getTaskDependencies().getDependencies(node));
             }
         }).add(this).findValues();
+
+
+        final HashSet<String> placeholderNames = new HashSet<String>(placeholders.keySet());
+        for (String placeholder : placeholderNames) {
+            maybeMaterializePlaceholder(placeholder);
+        }
+
+    }
+
+    public Map<String, Runnable> getPlaceholderActions() {
+        return placeholders;
+    }
+
+    public Task findByName(String name) {
+        Task task = super.findByName(name);
+        if (task != null) {
+            return task;
+        }
+        maybeMaterializePlaceholder(name);
+        return super.findByName(name);
+    }
+
+    private void maybeMaterializePlaceholder(String name) {
+        if (placeholders.containsKey(name)) {
+            if (super.findByName(name) == null) {
+                final Runnable placeholderAction = placeholders.remove(name);
+                placeholderAction.run();
+            }
+        }
+    }
+
+    public void addPlaceholderAction(String placeholderName, Runnable runnable) {
+        placeholders.put(placeholderName, runnable);
     }
 }
diff --git a/subprojects/core/src/main/groovy/org/gradle/api/internal/tasks/SimpleWorkResult.java b/subprojects/core/src/main/groovy/org/gradle/api/internal/tasks/SimpleWorkResult.java
new file mode 100644
index 0000000..5d52b22
--- /dev/null
+++ b/subprojects/core/src/main/groovy/org/gradle/api/internal/tasks/SimpleWorkResult.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.tasks;
+
+import org.gradle.api.tasks.WorkResult;
+
+public class SimpleWorkResult implements WorkResult {
+    private final boolean didWork;
+
+    public SimpleWorkResult(boolean didWork) {
+        this.didWork = didWork;
+    }
+
+    public boolean getDidWork() {
+        return didWork;
+    }
+}
diff --git a/subprojects/core/src/main/groovy/org/gradle/api/internal/tasks/TaskContainerInternal.java b/subprojects/core/src/main/groovy/org/gradle/api/internal/tasks/TaskContainerInternal.java
index 5a4e57c..c2ceef0 100644
--- a/subprojects/core/src/main/groovy/org/gradle/api/internal/tasks/TaskContainerInternal.java
+++ b/subprojects/core/src/main/groovy/org/gradle/api/internal/tasks/TaskContainerInternal.java
@@ -18,15 +18,31 @@ package org.gradle.api.internal.tasks;
 import org.gradle.api.internal.DynamicObject;
 import org.gradle.api.tasks.TaskContainer;
 
+import java.util.Map;
+
 public interface TaskContainerInternal extends TaskContainer, TaskResolver {
     DynamicObject getTasksAsDynamicObject();
 
     /**
+     * <p>Add placeholder action if task is referenced by name that does not (yet) exist.
+     * If a task is referenced by name and not listed as task, the provided action is executed and the task name is looked up again before proceeding
+     * This allows lazy application of plugins if task is referenced but not yet part of the taskcontainer.</p>
+     *
+     * @param placeholderName the placeholderName that references the placeholder action.
+     * @param runnable the Runnable executed when referencing a task that does not exist, but a placeholder with the given name is defined.
+     */
+    void addPlaceholderAction(String placeholderName, Runnable runnable);
+
+    /**
      * Force the entire graph to come into existence.
      *
-     * Tasks may have dependencies that are abstract (e.g. a dependency on a task _name_). Calling this method
-     * will force all task dependencies to be actualised, which may mean new tasks are created because of things
-     * like task rules etc.
+     * Tasks may have dependencies that are abstract (e.g. a dependency on a task _name_).
+     * Calling this method will force all task dependencies to be actualised, which may mean new tasks are
+     * created because of things like task rules etc.
+     *
+     * As part of this, all placeholder actions are materialized to show up in 'tasks' and 'tasks --all' overview.
      */
     void actualize();
+
+    Map<String, Runnable> getPlaceholderActions();
 }
diff --git a/subprojects/core/src/main/groovy/org/gradle/api/internal/tasks/TaskExecuter.java b/subprojects/core/src/main/groovy/org/gradle/api/internal/tasks/TaskExecuter.java
index 08aa32a..56a0310 100644
--- a/subprojects/core/src/main/groovy/org/gradle/api/internal/tasks/TaskExecuter.java
+++ b/subprojects/core/src/main/groovy/org/gradle/api/internal/tasks/TaskExecuter.java
@@ -22,5 +22,5 @@ public interface TaskExecuter {
      * Executes the given task. If the task fails with an exception, the exception is packaged in the provided task
      * state.
      */
-    void execute(TaskInternal task, TaskStateInternal state);
+    void execute(TaskInternal task, TaskStateInternal state, TaskExecutionContext context);
 }
diff --git a/subprojects/core/src/main/groovy/org/gradle/api/internal/tasks/TaskExecutionContext.java b/subprojects/core/src/main/groovy/org/gradle/api/internal/tasks/TaskExecutionContext.java
new file mode 100644
index 0000000..972c919
--- /dev/null
+++ b/subprojects/core/src/main/groovy/org/gradle/api/internal/tasks/TaskExecutionContext.java
@@ -0,0 +1,24 @@
+/*
+ * Copyright 2013 the original author or authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY 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.changedetection.TaskArtifactState;
+
+public interface TaskExecutionContext {
+    TaskArtifactState getTaskArtifactState();
+    void setTaskArtifactState(TaskArtifactState taskArtifactState);
+}
diff --git a/subprojects/core/src/main/groovy/org/gradle/api/internal/tasks/TaskStatusNagger.java b/subprojects/core/src/main/groovy/org/gradle/api/internal/tasks/TaskStatusNagger.java
index d9602f1..74533c0 100644
--- a/subprojects/core/src/main/groovy/org/gradle/api/internal/tasks/TaskStatusNagger.java
+++ b/subprojects/core/src/main/groovy/org/gradle/api/internal/tasks/TaskStatusNagger.java
@@ -17,7 +17,6 @@
 package org.gradle.api.internal.tasks;
 
 import groovy.util.ObservableList;
-import org.gradle.api.Action;
 import org.gradle.api.Task;
 import org.gradle.api.internal.TaskInternal;
 import org.gradle.util.DeprecationLogger;
@@ -70,8 +69,8 @@ public class TaskStatusNagger {
         }
     }
 
-    public Action<Task> leftShift(final Action<? super Task> action) {
-        return new Action<Task>() {
+    public ContextAwareTaskAction leftShift(final ContextAwareTaskAction action) {
+        return new ContextAwareTaskAction() {
             public void execute(Task task) {
                 executingleftShiftAction = true;
                 try {
@@ -80,6 +79,10 @@ public class TaskStatusNagger {
                     executingleftShiftAction = false;
                 }
             }
+
+            public void contextualise(TaskExecutionContext context) {
+                action.contextualise(context);
+            }
         };
     }
 
diff --git a/subprojects/core/src/main/groovy/org/gradle/api/internal/tasks/execution/DefaultTaskExecutionContext.java b/subprojects/core/src/main/groovy/org/gradle/api/internal/tasks/execution/DefaultTaskExecutionContext.java
new file mode 100644
index 0000000..8727b82
--- /dev/null
+++ b/subprojects/core/src/main/groovy/org/gradle/api/internal/tasks/execution/DefaultTaskExecutionContext.java
@@ -0,0 +1,31 @@
+/*
+ * Copyright 2013 the original author or authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY 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.changedetection.TaskArtifactState;
+import org.gradle.api.internal.tasks.TaskExecutionContext;
+
+public class DefaultTaskExecutionContext implements TaskExecutionContext {
+    private TaskArtifactState taskArtifactState;
+
+    public TaskArtifactState getTaskArtifactState() {
+        return taskArtifactState;
+    }
+
+    public void setTaskArtifactState(TaskArtifactState taskArtifactState) {
+        this.taskArtifactState = taskArtifactState;
+    }
+}
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
index d3b4dc5..7bad7bf 100644
--- 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
@@ -15,13 +15,10 @@
  */
 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.internal.tasks.*;
 import org.gradle.api.logging.Logger;
 import org.gradle.api.logging.Logging;
 import org.gradle.api.tasks.StopActionException;
@@ -42,11 +39,11 @@ public class ExecuteActionsTaskExecuter implements TaskExecuter {
         this.listener = listener;
     }
 
-    public void execute(TaskInternal task, TaskStateInternal state) {
+    public void execute(TaskInternal task, TaskStateInternal state, TaskExecutionContext context) {
         listener.beforeActions(task);
         state.setExecuting(true);
         try {
-            GradleException failure = executeActions(task, state);
+            GradleException failure = executeActions(task, state, context);
             state.executed(failure);
         } finally {
             state.setExecuting(false);
@@ -54,14 +51,14 @@ public class ExecuteActionsTaskExecuter implements TaskExecuter {
         }
     }
 
-    private GradleException executeActions(TaskInternal task, TaskStateInternal state) {
+    private GradleException executeActions(TaskInternal task, TaskStateInternal state, TaskExecutionContext context) {
         logger.debug("Executing actions for {}.", task);
-        final List<Action<? super Task>> actions = new ArrayList<Action<? super Task>>(task.getActions());
-        for (Action<? super Task> action : actions) {
+        final List<ContextAwareTaskAction> actions = new ArrayList<ContextAwareTaskAction>(task.getTaskActions());
+        for (ContextAwareTaskAction action : actions) {
             state.setDidWork(true);
             task.getStandardOutputCapture().start();
             try {
-                action.execute(task);
+                executeAction(task, action, context);
             } catch (StopActionException e) {
                 // Ignore
                 logger.debug("Action stopped by some action with message: {}", e.getMessage());
@@ -76,4 +73,13 @@ public class ExecuteActionsTaskExecuter implements TaskExecuter {
         }
         return null;
     }
+
+    private void executeAction(TaskInternal task, ContextAwareTaskAction action, TaskExecutionContext context) {
+        action.contextualise(context);
+        try {
+            action.execute(task);
+        } finally {
+            action.contextualise(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
index 9744ee5..a51fa5a 100644
--- 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
@@ -18,6 +18,7 @@ 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.TaskExecutionContext;
 import org.gradle.api.internal.tasks.TaskStateInternal;
 import org.gradle.api.logging.Logger;
 import org.gradle.api.logging.Logging;
@@ -33,13 +34,13 @@ public class ExecuteAtMostOnceTaskExecuter implements TaskExecuter {
         this.executer = executer;
     }
 
-    public void execute(TaskInternal task, TaskStateInternal state) {
+    public void execute(TaskInternal task, TaskStateInternal state, TaskExecutionContext context) {
         if (state.getExecuted()) {
             return;
         }
         LOGGER.debug("Starting to execute {}", task);
         try {
-            executer.execute(task, state);
+            executer.execute(task, state, context);
         } 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
index e866788..022448f 100644
--- 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
@@ -18,6 +18,7 @@ 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.TaskExecutionContext;
 import org.gradle.api.internal.tasks.TaskStateInternal;
 
 /**
@@ -30,8 +31,8 @@ public class PostExecutionAnalysisTaskExecuter implements TaskExecuter {
         this.executer = executer;
     }
 
-    public void execute(TaskInternal task, TaskStateInternal state) {
-        executer.execute(task, state);
+    public void execute(TaskInternal task, TaskStateInternal state, TaskExecutionContext context) {
+        executer.execute(task, state, context);
         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
index e847392..b6ecff7 100644
--- 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
@@ -17,6 +17,7 @@ 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.TaskExecutionContext;
 import org.gradle.api.internal.tasks.TaskStateInternal;
 import org.gradle.api.logging.Logger;
 import org.gradle.api.logging.Logging;
@@ -32,12 +33,12 @@ public class SkipEmptySourceFilesTaskExecuter implements TaskExecuter {
         this.executer = executer;
     }
 
-    public void execute(TaskInternal task, TaskStateInternal state) {
+    public void execute(TaskInternal task, TaskStateInternal state, TaskExecutionContext context) {
         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);
+        executer.execute(task, state, context);
     }
 }
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
index e56584d..8549818 100644
--- 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
@@ -19,6 +19,7 @@ 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.TaskExecutionContext;
 import org.gradle.api.internal.tasks.TaskStateInternal;
 import org.gradle.api.logging.Logger;
 import org.gradle.api.logging.Logging;
@@ -34,7 +35,7 @@ public class SkipOnlyIfTaskExecuter implements TaskExecuter {
         this.executer = executer;
     }
 
-    public void execute(TaskInternal task, TaskStateInternal state) {
+    public void execute(TaskInternal task, TaskStateInternal state, TaskExecutionContext context) {
         boolean skip;
         try {
             skip = !task.getOnlyIf().isSatisfiedBy(task);
@@ -49,6 +50,6 @@ public class SkipOnlyIfTaskExecuter implements TaskExecuter {
             return;
         }
 
-        executer.execute(task, state);
+        executer.execute(task, state, context);
     }
 }
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
index 088b2b6..5cf62a3 100644
--- 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
@@ -18,6 +18,7 @@ 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.TaskExecutionContext;
 import org.gradle.api.internal.tasks.TaskStateInternal;
 import org.gradle.api.logging.Logger;
 import org.gradle.api.logging.Logging;
@@ -33,7 +34,7 @@ public class SkipTaskWithNoActionsExecuter implements TaskExecuter {
         this.executer = executer;
     }
 
-    public void execute(TaskInternal task, TaskStateInternal state) {
+    public void execute(TaskInternal task, TaskStateInternal state, TaskExecutionContext context) {
         if (task.getActions().isEmpty()) {
             LOGGER.info("Skipping {} as it has no actions.", task);
             boolean upToDate = true;
@@ -48,6 +49,6 @@ public class SkipTaskWithNoActionsExecuter implements TaskExecuter {
             }
             return;
         }
-        executer.execute(task, state);
+        executer.execute(task, state, context);
     }
 }
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
index e478e61..297eaac 100644
--- 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
@@ -20,10 +20,16 @@ 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.TaskExecutionContext;
 import org.gradle.api.internal.tasks.TaskStateInternal;
+import org.gradle.util.Clock;
 import org.slf4j.Logger;
 import org.slf4j.LoggerFactory;
 
+import java.util.ArrayList;
+import java.util.Formatter;
+import java.util.List;
+
 /**
  * A {@link TaskExecuter} which skips tasks whose outputs are up-to-date.
  */
@@ -32,35 +38,52 @@ public class SkipUpToDateTaskExecuter implements TaskExecuter {
     private final TaskExecuter executer;
     private final TaskArtifactStateRepository repository;
 
-    public SkipUpToDateTaskExecuter(TaskExecuter executer, TaskArtifactStateRepository repository) {
+    public SkipUpToDateTaskExecuter(TaskArtifactStateRepository repository, TaskExecuter executer) {
         this.executer = executer;
         this.repository = repository;
     }
 
-    public void execute(TaskInternal task, TaskStateInternal state) {
+    public void execute(TaskInternal task, TaskStateInternal state, TaskExecutionContext context) {
         LOGGER.debug("Determining if {} is up-to-date", task);
+        Clock clock = new Clock();
         TaskArtifactState taskArtifactState = repository.getStateFor(task);
         try {
-            if (taskArtifactState.isUpToDate()) {
-                LOGGER.info("Skipping {} as it is up-to-date", task);
+            List<String> messages = new ArrayList<String>();
+            if (taskArtifactState.isUpToDate(messages)) {
+                LOGGER.info("Skipping {} as it is up-to-date (took {}).", task, clock.getTime());
                 state.upToDate();
                 return;
-
             }
-            LOGGER.debug("{} is not up-to-date", task);
+            logOutOfDateMessages(messages, task, clock.getTime());
 
-            taskArtifactState.beforeTask();
             task.getOutputs().setHistory(taskArtifactState.getExecutionHistory());
+            context.setTaskArtifactState(taskArtifactState);
+
+            taskArtifactState.beforeTask();
             try {
-                executer.execute(task, state);
+                executer.execute(task, state, context);
                 if (state.getFailure() == null) {
                     taskArtifactState.afterTask();
                 }
             } finally {
                 task.getOutputs().setHistory(null);
+                context.setTaskArtifactState(null);
             }
         } finally {
             taskArtifactState.finished();
         }
     }
+
+
+    private void logOutOfDateMessages(List<String> messages, TaskInternal task, String took) {
+        if (LOGGER.isInfoEnabled()) {
+            Formatter formatter = new Formatter();
+            formatter.format("Executing %s (up-to-date check took %s) due to:", task, took);
+            for (String message : messages) {
+                formatter.format("%n  %s", message);
+            }
+            LOGGER.info(formatter.toString());
+        }
+    }
+
 }
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
index 67927c2..63ca339 100644
--- 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
@@ -18,6 +18,7 @@ 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.TaskExecutionContext;
 import org.gradle.api.internal.tasks.TaskStateInternal;
 import org.gradle.api.tasks.TaskValidationException;
 
@@ -34,7 +35,7 @@ public class ValidatingTaskExecuter implements TaskExecuter {
         this.executer = executer;
     }
 
-    public void execute(TaskInternal task, TaskStateInternal state) {
+    public void execute(TaskInternal task, TaskStateInternal state, TaskExecutionContext context) {
         List<String> messages = new ArrayList<String>();
         for (TaskValidator validator : task.getValidators()) {
             validator.validate(task, messages);
@@ -54,6 +55,6 @@ public class ValidatingTaskExecuter implements TaskExecuter {
             state.executed(new TaskValidationException(errorMessage, causes));
             return;
         }
-        executer.execute(task, state);
+        executer.execute(task, state, context);
     }
 }
diff --git a/subprojects/core/src/main/groovy/org/gradle/api/internal/xml/SimpleXmlWriter.java b/subprojects/core/src/main/groovy/org/gradle/api/internal/xml/SimpleXmlWriter.java
index 73bf959..38cee90 100644
--- a/subprojects/core/src/main/groovy/org/gradle/api/internal/xml/SimpleXmlWriter.java
+++ b/subprojects/core/src/main/groovy/org/gradle/api/internal/xml/SimpleXmlWriter.java
@@ -23,8 +23,6 @@ import java.io.Writer;
 
 /**
  * <p>A streaming XML writer.</p>
- *
- * by Szczepan Faber, created at: 12/3/12
  */
 public class SimpleXmlWriter extends SimpleMarkupWriter {
 
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 2588fad..47576e8 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
@@ -23,6 +23,7 @@ import org.gradle.api.Project;
 import org.gradle.api.ProjectEvaluationListener;
 import org.gradle.api.execution.TaskExecutionGraph;
 import org.gradle.api.internal.HasInternalProtocol;
+import org.gradle.api.plugins.PluginAware;
 
 import java.io.File;
 
@@ -32,7 +33,7 @@ import java.io.File;
  * <p>You can obtain a {@code Gradle} instance by calling {@link Project#getGradle()}.</p>
  */
 @HasInternalProtocol
-public interface Gradle {
+public interface Gradle extends PluginAware {
     /**
      * Returns the current Gradle version.
      *
diff --git a/subprojects/core/src/main/groovy/org/gradle/api/logging/LogLevel.java b/subprojects/core/src/main/groovy/org/gradle/api/logging/LogLevel.java
index 4107c32..1935167 100644
--- a/subprojects/core/src/main/groovy/org/gradle/api/logging/LogLevel.java
+++ b/subprojects/core/src/main/groovy/org/gradle/api/logging/LogLevel.java
@@ -17,8 +17,6 @@ package org.gradle.api.logging;
 
 /**
  * The log levels supported by Gradle.
- *
- * @author Hans Dockter
  */
 public enum LogLevel {
     DEBUG {
diff --git a/subprojects/core/src/main/groovy/org/gradle/api/logging/Logging.java b/subprojects/core/src/main/groovy/org/gradle/api/logging/Logging.java
index 0d60dc2..7d2b455 100644
--- a/subprojects/core/src/main/groovy/org/gradle/api/logging/Logging.java
+++ b/subprojects/core/src/main/groovy/org/gradle/api/logging/Logging.java
@@ -27,8 +27,6 @@ import java.util.Map;
 /**
  * <p>The main entry point for Gradle's logging system. Gradle routes all logging via SLF4J. You can use either an SLF4J
  * {@link org.slf4j.Logger} or a Gradle {@link Logger} to perform logging.</p>
- *
- * @author Hans Dockter
  */
 public class Logging {
     public static final Marker LIFECYCLE = MarkerFactory.getDetachedMarker("LIFECYCLE");
diff --git a/subprojects/core/src/main/groovy/org/gradle/api/plugins/Convention.java b/subprojects/core/src/main/groovy/org/gradle/api/plugins/Convention.java
index b38dad3..7256711 100644
--- a/subprojects/core/src/main/groovy/org/gradle/api/plugins/Convention.java
+++ b/subprojects/core/src/main/groovy/org/gradle/api/plugins/Convention.java
@@ -24,8 +24,6 @@ import java.util.Map;
  * Convention}, and the properties and methods of the convention object become available as properties and methods of
  * the object which the convention is associated to. A convention object is simply a POJO or POGO. Usually, a {@code
  * Convention} is used by plugins to extend a {@link org.gradle.api.Project} or a {@link org.gradle.api.Task}.</p>
- *
- * @author Hans Dockter
  */
 public interface Convention extends ExtensionContainer {
 
diff --git a/subprojects/core/src/main/groovy/org/gradle/api/plugins/ExtensionContainer.java b/subprojects/core/src/main/groovy/org/gradle/api/plugins/ExtensionContainer.java
index 0c8edbb..9835707 100644
--- a/subprojects/core/src/main/groovy/org/gradle/api/plugins/ExtensionContainer.java
+++ b/subprojects/core/src/main/groovy/org/gradle/api/plugins/ExtensionContainer.java
@@ -19,10 +19,12 @@ package org.gradle.api.plugins;
 import org.gradle.api.Action;
 import org.gradle.api.Incubating;
 import org.gradle.api.UnknownDomainObjectException;
+import org.gradle.api.internal.HasInternalProtocol;
 
 /**
  * Allows adding 'namespaced' DSL extensions to a target object.
  */
+ at HasInternalProtocol
 public interface ExtensionContainer {
 
     /**
diff --git a/subprojects/core/src/main/groovy/org/gradle/api/plugins/PluginAware.java b/subprojects/core/src/main/groovy/org/gradle/api/plugins/PluginAware.java
new file mode 100644
index 0000000..2dd774b
--- /dev/null
+++ b/subprojects/core/src/main/groovy/org/gradle/api/plugins/PluginAware.java
@@ -0,0 +1,62 @@
+/*
+ * Copyright 2013 the original author or authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF 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 groovy.lang.Closure;
+
+import java.util.Map;
+
+/**
+ * Objects a {@link org.gradle.api.Plugin} can be applied to.
+ *
+ * <p>
+ * For more on writing and applying plugins, see {@link org.gradle.api.Plugin}.
+ * </p>
+ */
+public interface PluginAware {
+    /**
+     * Returns the plugins container for this object. The returned container can be used to manage the plugins which
+     * are used by this object.
+     *
+     * @return the plugin container. Never returns null.
+     */
+    PluginContainer getPlugins();
+
+    /**
+     * <p>Configures this object using plugins or scripts. The given closure is used to configure an {@link
+     * ObjectConfigurationAction} which is then used to configure this object.</p>
+     *
+     * @param closure The closure to configure the {@code ObjectConfigurationAction}.
+     */
+    void apply(Closure closure);
+
+    /**
+     * <p>Configures this Object using plugins or scripts. The following options are available:</p>
+     *
+     * <ul><li>{@code from}: A script to apply to the object. Accepts any path supported by {@link org.gradle.api.Project#uri(Object)}.</li>
+     *
+     * <li>{@code plugin}: The id or implementation class of the plugin to apply to the object.</li>
+     *
+     * <li>{@code to}: The target delegate object or objects. Use this to configure objects other than this
+     * object.</li></ul>
+     *
+     * <p>For more detail, see {@link ObjectConfigurationAction}.</p>
+     *
+     * @param options The options to use to configure the {@code ObjectConfigurationAction}.
+     */
+    void apply(Map<String, ?> options);
+}
diff --git a/subprojects/core/src/main/groovy/org/gradle/api/plugins/PluginCollection.java b/subprojects/core/src/main/groovy/org/gradle/api/plugins/PluginCollection.java
index 1430c64..99e5b24 100644
--- a/subprojects/core/src/main/groovy/org/gradle/api/plugins/PluginCollection.java
+++ b/subprojects/core/src/main/groovy/org/gradle/api/plugins/PluginCollection.java
@@ -15,17 +15,15 @@
  */
 package org.gradle.api.plugins;
 
-import org.gradle.api.DomainObjectSet;
-import org.gradle.api.specs.Spec;
+import groovy.lang.Closure;
 import org.gradle.api.Action;
+import org.gradle.api.DomainObjectSet;
 import org.gradle.api.Plugin;
-
-import groovy.lang.Closure;
+import org.gradle.api.specs.Spec;
 
 /**
  * <p>A {@code PluginCollection} represents a collection of {@link org.gradle.api.Plugin} instances.</p>
  * 
- * @author Hans Dockter
  * @param <T> The type of plugins which this collection contains.
  */
 public interface PluginCollection<T extends Plugin> extends DomainObjectSet<T> {
diff --git a/subprojects/core/src/main/groovy/org/gradle/api/plugins/PluginContainer.java b/subprojects/core/src/main/groovy/org/gradle/api/plugins/PluginContainer.java
index c90d288..ef8025f 100644
--- a/subprojects/core/src/main/groovy/org/gradle/api/plugins/PluginContainer.java
+++ b/subprojects/core/src/main/groovy/org/gradle/api/plugins/PluginContainer.java
@@ -24,8 +24,6 @@ import org.gradle.api.Plugin;
  *
  * <p>Plugins can be specified using either an id or type. The id of a plugin is specified using a
  * META-INF/gradle-plugins/${id}.properties classpath resource.</p>
- *
- * @author Hans Dockter
  */
 public interface PluginContainer extends PluginCollection<Plugin> {
     /**
diff --git a/subprojects/core/src/main/groovy/org/gradle/api/plugins/PluginInstantiationException.java b/subprojects/core/src/main/groovy/org/gradle/api/plugins/PluginInstantiationException.java
index 914d3fd..9d5cc54 100644
--- a/subprojects/core/src/main/groovy/org/gradle/api/plugins/PluginInstantiationException.java
+++ b/subprojects/core/src/main/groovy/org/gradle/api/plugins/PluginInstantiationException.java
@@ -19,8 +19,6 @@ import org.gradle.api.GradleException;
 
 /**
  * A {@code PluginInstantiationException} is thrown when a plugin cannot be instantiated.
- *
- * @author Hans Dockter
  */
 public class PluginInstantiationException extends GradleException {
     public PluginInstantiationException(String message) {
diff --git a/subprojects/core/src/main/groovy/org/gradle/api/plugins/UnknownPluginException.java b/subprojects/core/src/main/groovy/org/gradle/api/plugins/UnknownPluginException.java
index fce0920..a0c1c19 100644
--- a/subprojects/core/src/main/groovy/org/gradle/api/plugins/UnknownPluginException.java
+++ b/subprojects/core/src/main/groovy/org/gradle/api/plugins/UnknownPluginException.java
@@ -19,8 +19,6 @@ import org.gradle.api.InvalidUserDataException;
 
 /**
  * A {@code UnknownPluginException} is thrown when an unknown plugin id is provided. 
- *
- * @author Hans Dockter
  */
 public class UnknownPluginException extends InvalidUserDataException {
     public UnknownPluginException(String message) {
diff --git a/subprojects/core/src/main/groovy/org/gradle/api/specs/Specs.java b/subprojects/core/src/main/groovy/org/gradle/api/specs/Specs.java
index bf6c8d8..0430238 100644
--- a/subprojects/core/src/main/groovy/org/gradle/api/specs/Specs.java
+++ b/subprojects/core/src/main/groovy/org/gradle/api/specs/Specs.java
@@ -25,8 +25,6 @@ import java.util.Set;
 
 /**
  * Provides a number of {@link org.gradle.api.specs.Spec} implementations.
- *
- * @author Hans Dockter
  */
 public class Specs {
 
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 6b610b8..cb3630f 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
@@ -19,11 +19,12 @@ import groovy.lang.Closure;
 import org.gradle.api.Action;
 import org.gradle.api.file.*;
 import org.gradle.api.internal.ConventionTask;
-import org.gradle.api.internal.file.copy.CopyActionImpl;
-import org.gradle.api.internal.file.copy.CopySpecSource;
-import org.gradle.api.internal.file.copy.ReadableCopySpec;
+import org.gradle.api.internal.file.FileResolver;
+import org.gradle.api.internal.file.copy.*;
 import org.gradle.api.specs.Spec;
 import org.gradle.internal.Factory;
+import org.gradle.internal.nativeplatform.filesystem.FileSystem;
+import org.gradle.internal.reflect.Instantiator;
 import org.gradle.util.DeprecationLogger;
 
 import java.io.FilterReader;
@@ -36,15 +37,37 @@ import java.util.regex.Pattern;
  */
 public abstract class AbstractCopyTask extends ConventionTask implements CopySpec, CopySpecSource {
 
+    private final CopySpecInternal rootSpec;
+    private final CopySpecInternal mainSpec;
+
+    protected AbstractCopyTask() {
+        this.rootSpec = createRootSpec();
+        this.mainSpec = rootSpec.addChild();
+    }
+
+    protected CopySpecInternal createRootSpec() {
+        Instantiator instantiator = getServices().get(Instantiator.class);
+        FileResolver fileResolver = getServices().get(FileResolver.class);
+        return instantiator.newInstance(DefaultCopySpec.class, fileResolver, instantiator);
+    }
+
+    protected abstract CopyAction createCopyAction();
+
     @TaskAction
     protected void copy() {
         configureRootSpec();
-        getCopyAction().execute();
-        setDidWork(getCopyAction().getDidWork());
+
+        Instantiator instantiator = getServices().get(Instantiator.class);
+        FileSystem fileSystem = getServices().get(FileSystem.class);
+
+        CopyActionExecuter copyActionExecuter = new CopyActionExecuter(instantiator, fileSystem);
+        CopyAction copyAction = createCopyAction();
+        WorkResult didWork = copyActionExecuter.execute(rootSpec, copyAction);
+        setDidWork(didWork.getDidWork());
     }
 
     protected void configureRootSpec() {
-        if (!getCopyAction().hasSource()) {
+        if (!rootSpec.hasSource()) {
             Object srcDirs = getDefaultSource();
             if (srcDirs != null) {
                 from(srcDirs);
@@ -68,8 +91,8 @@ public abstract class AbstractCopyTask extends ConventionTask implements CopySpe
      */
     @InputFiles @SkipWhenEmpty @Optional
     public FileCollection getSource() {
-        if(getCopyAction().hasSource()){
-            return getCopyAction().getAllSource();
+        if (rootSpec.hasSource()){
+            return rootSpec.getAllSource();
         }else{
             return DeprecationLogger.whileDisabled(new Factory<FileCollection>() {
                 public FileCollection create() {
@@ -78,19 +101,18 @@ public abstract class AbstractCopyTask extends ConventionTask implements CopySpe
             });
         }
     }
-    
-    protected abstract CopyActionImpl getCopyAction();
 
-    public ReadableCopySpec getRootSpec() {
-        return getCopyAction().getRootSpec();
+
+    public CopySpecInternal getRootSpec() {
+        return rootSpec;
     }
 
     // -----------------------------------------------
     // ---- Delegate CopySpec methods to rootSpec ----
     // -----------------------------------------------
 
-    protected CopySpec getMainSpec() {
-        return getCopyAction();
+    protected CopySpecInternal getMainSpec() {
+        return mainSpec;
     }
 
     /**
@@ -124,6 +146,20 @@ public abstract class AbstractCopyTask extends ConventionTask implements CopySpe
     /**
      * {@inheritDoc}
      */
+    public void setDuplicatesStrategy(DuplicatesStrategy strategy) {
+        getRootSpec().setDuplicatesStrategy(strategy);
+    }
+
+    /**
+     * {@inheritDoc}
+     */
+    public DuplicatesStrategy getDuplicatesStrategy() {
+        return getRootSpec().getDuplicatesStrategy();
+    }
+
+    /**
+     * {@inheritDoc}
+     */
     public AbstractCopyTask from(Object... sourcePaths) {
         getMainSpec().from(sourcePaths);
         return this;
@@ -132,6 +168,22 @@ public abstract class AbstractCopyTask extends ConventionTask implements CopySpe
     /**
      * {@inheritDoc}
      */
+    public AbstractCopyTask filesMatching(String pattern, Action<? super FileCopyDetails> action) {
+        getMainSpec().filesMatching(pattern, action);
+        return this;
+    }
+
+    /**
+     * {@inheritDoc}
+     */
+    public AbstractCopyTask filesNotMatching(String pattern, Action<? super FileCopyDetails> action) {
+        getMainSpec().filesNotMatching(pattern, action);
+        return this;
+    }
+
+    /**
+     * {@inheritDoc}
+     */
     public AbstractCopyTask from(Object sourcePath, Closure c) {
         getMainSpec().from(sourcePath, c);
         return this;
@@ -149,7 +201,7 @@ public abstract class AbstractCopyTask extends ConventionTask implements CopySpe
      * {@inheritDoc}
      */
     public AbstractCopyTask into(Object destDir) {
-        getMainSpec().into(destDir);
+        getRootSpec().into(destDir);
         return this;
     }
 
@@ -356,4 +408,5 @@ public abstract class AbstractCopyTask extends ConventionTask implements CopySpe
         getMainSpec().eachFile(closure);
         return this;
     }
+
 }
diff --git a/subprojects/core/src/main/groovy/org/gradle/api/tasks/AntBuilderAware.groovy b/subprojects/core/src/main/groovy/org/gradle/api/tasks/AntBuilderAware.groovy
index d1a9b5b..9031780 100644
--- a/subprojects/core/src/main/groovy/org/gradle/api/tasks/AntBuilderAware.groovy
+++ b/subprojects/core/src/main/groovy/org/gradle/api/tasks/AntBuilderAware.groovy
@@ -19,8 +19,6 @@ package org.gradle.api.tasks
 /**
  * An {@code AntBuilderAware} represents an object which can add itself to Ant tasks, using an
  * {@link org.gradle.api.AntBuilder}.
- *
- * @author Hans Dockter
  */
 interface AntBuilderAware {
     def addToAntBuilder(node, String childNodeName)
diff --git a/subprojects/core/src/main/groovy/org/gradle/api/tasks/ConventionValue.java b/subprojects/core/src/main/groovy/org/gradle/api/tasks/ConventionValue.java
index 4db9eb5..7cd2625 100644
--- a/subprojects/core/src/main/groovy/org/gradle/api/tasks/ConventionValue.java
+++ b/subprojects/core/src/main/groovy/org/gradle/api/tasks/ConventionValue.java
@@ -23,7 +23,6 @@ import org.gradle.api.plugins.Convention;
  * A ConventionValue can be assigned to a {@link org.gradle.api.internal.IConventionAware} task. If a property of such an object is not set internally, a ConventionValue is used to calculate the value
  * for the property.
  *
- * @author Hans Dockter
  * @deprecated Use {@link groovy.lang.Closure} or {@link java.util.concurrent.Callable} instead.
  */
 @Deprecated
diff --git a/subprojects/core/src/main/groovy/org/gradle/api/tasks/Copy.java b/subprojects/core/src/main/groovy/org/gradle/api/tasks/Copy.java
index 1cdf48b..295d77d 100644
--- a/subprojects/core/src/main/groovy/org/gradle/api/tasks/Copy.java
+++ b/subprojects/core/src/main/groovy/org/gradle/api/tasks/Copy.java
@@ -16,9 +16,15 @@
 
 package org.gradle.api.tasks;
 
+import org.gradle.api.InvalidUserDataException;
+import org.gradle.api.internal.file.BaseDirFileResolver;
 import org.gradle.api.internal.file.FileResolver;
-import org.gradle.api.internal.file.copy.FileCopyActionImpl;
-import org.gradle.api.internal.file.copy.FileCopySpecVisitor;
+import org.gradle.api.internal.file.copy.CopyAction;
+import org.gradle.api.internal.file.copy.CopySpecInternal;
+import org.gradle.api.internal.file.copy.DestinationRootCopySpec;
+import org.gradle.api.internal.file.copy.FileCopyAction;
+import org.gradle.internal.nativeplatform.filesystem.FileSystems;
+import org.gradle.internal.reflect.Instantiator;
 
 import java.io.File;
 
@@ -29,58 +35,54 @@ import java.io.File;
  * <p> Examples:
  * <pre autoTested=''>
  * task mydoc(type:Copy) {
- *    from 'src/main/doc'
- *    into 'build/target/doc'
+ * from 'src/main/doc'
+ * into 'build/target/doc'
  * }
  *
  * //for ant filter
  * import org.apache.tools.ant.filters.ReplaceTokens
  *
  * task initconfig(type:Copy) {
- *    from('src/main/config') {
- *       include '**/*.properties'
- *       include '**/*.xml'
- *       filter(ReplaceTokens, tokens:[version:'2.3.1'])
- *    }
- *    from('src/main/config') {
- *       exclude '**/*.properties', '**/*.xml'
- *    }
- *    from('src/main/languages') {
- *       rename 'EN_US_(.*)', '$1'
- *    }
- *    into 'build/target/config'
- *    exclude '**/*.bak'
+ * from('src/main/config') {
+ * include '**/*.properties'
+ * include '**/*.xml'
+ * filter(ReplaceTokens, tokens:[version:'2.3.1'])
+ * }
+ * from('src/main/config') {
+ * exclude '**/*.properties', '**/*.xml'
+ * }
+ * from('src/main/languages') {
+ * rename 'EN_US_(.*)', '$1'
+ * }
+ * into 'build/target/config'
+ * exclude '**/*.bak'
  *
- *    includeEmptyDirs = false
+ * includeEmptyDirs = false
  * }
  * </pre>
- *
- * @author Steve Appling
  */
 public class Copy extends AbstractCopyTask {
-    private FileCopyActionImpl copyAction;
-
-    public Copy() {
-        FileResolver fileResolver = getServices().get(FileResolver.class);
-        copyAction = new FileCopyActionImpl(fileResolver, new FileCopySpecVisitor());
-    }
 
-    protected void configureRootSpec() {
-        super.configureRootSpec();
-        if (getCopyAction().getDestinationDir() == null) {
-            File destDir = getDestinationDir();
-            if (destDir != null) {
-                into(destDir);
-            }
+    @Override
+    protected CopyAction createCopyAction() {
+        File destinationDir = getDestinationDir();
+        if (destinationDir == null) {
+            throw new InvalidUserDataException("No copy destination directory has been specified, use 'into' to specify a target directory.");
         }
+        return new FileCopyAction(new BaseDirFileResolver(FileSystems.getDefault(), destinationDir));
     }
 
-    public FileCopyActionImpl getCopyAction() {
-        return copyAction;
+    @Override
+    protected CopySpecInternal createRootSpec() {
+        Instantiator instantiator = getServices().get(Instantiator.class);
+        FileResolver fileResolver = getServices().get(FileResolver.class);
+
+        return instantiator.newInstance(DestinationRootCopySpec.class, fileResolver, super.createRootSpec());
     }
 
-    public void setCopyAction(FileCopyActionImpl copyAction) {
-        this.copyAction = copyAction;
+    @Override
+    public DestinationRootCopySpec getRootSpec() {
+        return (DestinationRootCopySpec) super.getRootSpec();
     }
 
     /**
@@ -90,7 +92,7 @@ public class Copy extends AbstractCopyTask {
      */
     @OutputDirectory
     public File getDestinationDir() {
-        return getCopyAction().getDestinationDir();
+        return getRootSpec().getDestinationDir();
     }
 
     /**
@@ -101,4 +103,5 @@ public class Copy extends AbstractCopyTask {
     public void setDestinationDir(File destinationDir) {
         into(destinationDir);
     }
+
 }
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 531ec76..f09ef9d 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
@@ -29,8 +29,6 @@ import java.util.Set;
  *   delete 'uglyFolder', 'uglyFile'
  * }
  * </pre>
- *
- * @author Hans Dockter
  */
 public class Delete extends ConventionTask {
     private Set<Object> delete = new LinkedHashSet<Object>();
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 13b2e11..e679f10 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
@@ -50,8 +50,6 @@ import java.util.Map;
  *   }
  * }
  * </pre>
- * 
- * @author Hans Dockter
  */
 public class Exec extends ConventionTask implements ExecSpec {
     private ExecAction execAction;
diff --git a/subprojects/core/src/main/groovy/org/gradle/api/tasks/GradleBuild.java b/subprojects/core/src/main/groovy/org/gradle/api/tasks/GradleBuild.java
index ecde020..06fc04c 100755
--- a/subprojects/core/src/main/groovy/org/gradle/api/tasks/GradleBuild.java
+++ b/subprojects/core/src/main/groovy/org/gradle/api/tasks/GradleBuild.java
@@ -15,10 +15,11 @@
  */
 package org.gradle.api.tasks;
 
-import org.gradle.GradleLauncher;
 import org.gradle.StartParameter;
 import org.gradle.api.internal.ConventionTask;
+import org.gradle.initialization.GradleLauncherFactory;
 
+import javax.inject.Inject;
 import java.io.File;
 import java.util.Collection;
 import java.util.List;
@@ -27,10 +28,13 @@ import java.util.List;
  * Executes a Gradle build.
  */
 public class GradleBuild extends ConventionTask {
+    private final GradleLauncherFactory gradleLauncherFactory;
     private StartParameter startParameter;
 
-    public GradleBuild() {
-        this.startParameter = getProject().getGradle().getStartParameter().newBuild();
+    @Inject
+    public GradleBuild(StartParameter currentBuild, GradleLauncherFactory gradleLauncherFactory) {
+        this.gradleLauncherFactory = gradleLauncherFactory;
+        this.startParameter = currentBuild.newBuild();
         startParameter.setCurrentDir(getProject().getProjectDir());
     }
 
@@ -109,6 +113,6 @@ public class GradleBuild extends ConventionTask {
 
     @TaskAction
     void build() {
-        GradleLauncher.newInstance(getStartParameter()).run().rethrowFailure();
+        gradleLauncherFactory.newInstance(getStartParameter()).run().rethrowFailure();
     }
 }
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 541830d..f8c496c 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
@@ -33,8 +33,6 @@ import java.util.Map;
 
 /**
  * Executes a Java application in a child process.
- *
- * @author Hans Dockter
  */
 public class JavaExec extends ConventionTask implements JavaExecSpec {
     private JavaExecAction javaExecHandleBuilder;
@@ -46,7 +44,8 @@ public class JavaExec extends ConventionTask implements JavaExecSpec {
 
     @TaskAction
     public void exec() {
-        setMain(getMain()); // make convention mapping work (at least for 'main')
+        setMain(getMain()); // make convention mapping work (at least for 'main'...
+        setJvmArgs(getJvmArgs()); // ...and for 'jvmArgs')
         javaExecHandleBuilder.execute();
     }
 
diff --git a/subprojects/core/src/main/groovy/org/gradle/api/tasks/StopActionException.java b/subprojects/core/src/main/groovy/org/gradle/api/tasks/StopActionException.java
index 3def548..7eb97ea 100644
--- a/subprojects/core/src/main/groovy/org/gradle/api/tasks/StopActionException.java
+++ b/subprojects/core/src/main/groovy/org/gradle/api/tasks/StopActionException.java
@@ -32,8 +32,6 @@ import org.gradle.api.GradleException;
  * need the if statement.</p>
  *
  * <p>Note that throwing this exception does not fail the execution of the task or the build.</p>
- *
- * @author Hans Dockter
  */
 public class StopActionException extends GradleException {
     public StopActionException() {
diff --git a/subprojects/core/src/main/groovy/org/gradle/api/tasks/StopExecutionException.java b/subprojects/core/src/main/groovy/org/gradle/api/tasks/StopExecutionException.java
index 233e3f1..9900845 100644
--- a/subprojects/core/src/main/groovy/org/gradle/api/tasks/StopExecutionException.java
+++ b/subprojects/core/src/main/groovy/org/gradle/api/tasks/StopExecutionException.java
@@ -22,8 +22,6 @@ package org.gradle.api.tasks;
  * actions to be added to a task which abort execution of the task if the preconditions are not met.</p>
  *
  * <p>Note that throwing this exception does not fail the execution of the task or the build.</p>
- *
- * @author Hans Dockter
  */
 public class StopExecutionException extends RuntimeException {
 
diff --git a/subprojects/core/src/main/groovy/org/gradle/api/tasks/Sync.java b/subprojects/core/src/main/groovy/org/gradle/api/tasks/Sync.java
index 7f4e22a..60773aa 100644
--- a/subprojects/core/src/main/groovy/org/gradle/api/tasks/Sync.java
+++ b/subprojects/core/src/main/groovy/org/gradle/api/tasks/Sync.java
@@ -16,10 +16,12 @@
 
 package org.gradle.api.tasks;
 
+import org.gradle.api.InvalidUserDataException;
+import org.gradle.api.internal.file.BaseDirFileResolver;
 import org.gradle.api.internal.file.FileResolver;
-import org.gradle.api.internal.file.copy.FileCopyActionImpl;
-import org.gradle.api.internal.file.copy.FileCopySpecVisitor;
-import org.gradle.api.internal.file.copy.SyncCopySpecVisitor;
+import org.gradle.api.internal.file.copy.*;
+import org.gradle.internal.nativeplatform.filesystem.FileSystems;
+import org.gradle.internal.reflect.Instantiator;
 
 import java.io.File;
 
@@ -27,16 +29,27 @@ import java.io.File;
  * Synchronises the contents of a destination directory with some source directories and files.
  */
 public class Sync extends AbstractCopyTask {
-    private FileCopyActionImpl action;
 
-    public Sync() {
+    @Override
+    protected CopyAction createCopyAction() {
+        File destinationDir = getDestinationDir();
+        if (destinationDir == null) {
+            throw new InvalidUserDataException("No copy destination directory has been specified, use 'into' to specify a target directory.");
+        }
+        return new SyncCopyActionDecorator(destinationDir, new FileCopyAction(new BaseDirFileResolver(FileSystems.getDefault(), destinationDir)));
+    }
+
+    @Override
+    protected CopySpecInternal createRootSpec() {
+        Instantiator instantiator = getServices().get(Instantiator.class);
         FileResolver fileResolver = getServices().get(FileResolver.class);
-        action = new FileCopyActionImpl(fileResolver, new SyncCopySpecVisitor(new FileCopySpecVisitor()));
+
+        return instantiator.newInstance(DestinationRootCopySpec.class, fileResolver, super.createRootSpec());
     }
 
     @Override
-    protected FileCopyActionImpl getCopyAction() {
-        return action;
+    public DestinationRootCopySpec getRootSpec() {
+        return (DestinationRootCopySpec) super.getRootSpec();
     }
 
     /**
@@ -46,7 +59,7 @@ public class Sync extends AbstractCopyTask {
      */
     @OutputDirectory
     public File getDestinationDir() {
-        return getCopyAction().getDestinationDir();
+        return getRootSpec().getDestinationDir();
     }
 
     /**
@@ -57,4 +70,5 @@ public class Sync extends AbstractCopyTask {
     public void setDestinationDir(File destinationDir) {
         into(destinationDir);
     }
+
 }
diff --git a/subprojects/core/src/main/groovy/org/gradle/api/tasks/TaskContainer.java b/subprojects/core/src/main/groovy/org/gradle/api/tasks/TaskContainer.java
index 266042e..f7168aa 100644
--- a/subprojects/core/src/main/groovy/org/gradle/api/tasks/TaskContainer.java
+++ b/subprojects/core/src/main/groovy/org/gradle/api/tasks/TaskContainer.java
@@ -26,7 +26,7 @@ import java.util.Map;
  * <p>You can obtain a {@code TaskContainer} instance by calling {@link org.gradle.api.Project#getTasks()}, or using the
  * {@code tasks} property in your build script.</p>
  */
-public interface TaskContainer extends TaskCollection<Task>, NamedDomainObjectContainer<Task> {
+public interface TaskContainer extends TaskCollection<Task>, PolymorphicDomainObjectContainer<Task> {
     /**
      * <p>Locates a task by path. You can supply a task name, a relative path, or an absolute path. Relative paths are
      * interpreted relative to the project for this container. This method returns null if no task with the given path
@@ -62,7 +62,7 @@ public interface TaskContainer extends TaskCollection<Task>, NamedDomainObjectCo
      * <tr><td><code>{@value org.gradle.api.Task#TASK_TYPE}</code></td><td>The class of the task to
      * create.</td><td>{@link org.gradle.api.DefaultTask}</td></tr>
      *
-     * <tr><td><code>{@value org.gradle.api.Task#TASK_ACTION}</code></td><td>The closure or {@link TaskAction} to
+     * <tr><td><code>{@value org.gradle.api.Task#TASK_ACTION}</code></td><td>The closure or {@link Action} to
      * execute when the task executes. See {@link Task#doFirst(Action)}.</td><td><code>null</code></td></tr>
      *
      * <tr><td><code>{@value org.gradle.api.Task#TASK_OVERWRITE}</code></td><td>Replace an existing
@@ -76,13 +76,52 @@ public interface TaskContainer extends TaskCollection<Task>, NamedDomainObjectCo
      * <p>After the task is added, it is made available as a property of the project, so that you can reference the task
      * by name in your build file.  See <a href="../Project.html#properties">here</a> for more details.</p>
      *
-     * <p>If a task with the given name already exists in this container and the <code>overwrite</code> option is not set
-     * to true, an exception is thrown.</p>
+     * <p>If a task with the given name already exists in this container and the <code>{@value org.gradle.api.Task#TASK_OVERWRITE}</code>
+     * option is not set to true, an exception is thrown.</p>
      *
      * @param options The task creation options.
      * @return The newly created task object
      * @throws InvalidUserDataException If a task with the given name already exists in this project.
      */
+    Task create(Map<String, ?> options) throws InvalidUserDataException;
+
+    /**
+     * <p>Creates a {@link Task} and adds it to this container. A map of creation options can be passed to this method
+     * to control how the task is created. The following options are available:</p>
+     *
+     * <table>
+     *
+     * <tr><th>Option</th><th>Description</th><th>Default Value</th></tr>
+     *
+     * <tr><td><code>{@value org.gradle.api.Task#TASK_NAME}</code></td><td>The name of the task to create.</td><td>None.
+     * Must be specified.</td></tr>
+     *
+     * <tr><td><code>{@value org.gradle.api.Task#TASK_TYPE}</code></td><td>The class of the task to
+     * create.</td><td>{@link org.gradle.api.DefaultTask}</td></tr>
+     *
+     * <tr><td><code>{@value org.gradle.api.Task#TASK_ACTION}</code></td><td>The closure or {@link Action} to
+     * execute when the task executes. See {@link Task#doFirst(Action)}.</td><td><code>null</code></td></tr>
+     *
+     * <tr><td><code>{@value org.gradle.api.Task#TASK_OVERWRITE}</code></td><td>Replace an existing
+     * task?</td><td><code>false</code></td></tr>
+     *
+     * <tr><td><code>{@value org.gradle.api.Task#TASK_DEPENDS_ON}</code></td><td>The dependencies of the task. See <a
+     * href="../Task.html#dependencies">here</a> for more details.</td><td><code>[]</code></td></tr>
+     *
+     * </table>
+     *
+     * <p>After the task is added, it is made available as a property of the project, so that you can reference the task
+     * by name in your build file.  See <a href="../Project.html#properties">here</a> for more details.</p>
+     *
+     * <p>If a task with the given name already exists in this container and the <code>{@value org.gradle.api.Task#TASK_OVERWRITE}</code>
+     * option is not set to true, an exception is thrown.</p>
+     *
+     * @param options The task creation options.
+     * @return The newly created task object
+     * @throws InvalidUserDataException If a task with the given name already exists in this project.
+     * @deprecated use {@link #create(java.util.Map)} instead
+     */
+    @Deprecated
     Task add(Map<String, ?> options) throws InvalidUserDataException;
 
     /**
@@ -98,6 +137,23 @@ public interface TaskContainer extends TaskCollection<Task>, NamedDomainObjectCo
      * @return The newly created task object
      * @throws InvalidUserDataException If a task with the given name already exists in this project.
      */
+    Task create(Map<String, ?> options, Closure configureClosure) throws InvalidUserDataException;
+
+    /**
+     * <p>Creates a {@link Task} adds it to this container. A map of creation options can be passed to this method to
+     * control how the task is created. See {@link #add(java.util.Map)} for the list of options available. The given
+     * closure is used to configure the task before it is returned by this method.</p>
+     *
+     * <p>After the task is added, it is made available as a property of the project, so that you can reference the task
+     * by name in your build file. See <a href="../Project.html#properties">here</a> for more details.</p>
+     *
+     * @param options The task creation options.
+     * @param configureClosure The closure to use to configure the task.
+     * @return The newly created task object
+     * @throws InvalidUserDataException If a task with the given name already exists in this project.
+     * @deprecated use {@link #create(java.util.Map, groovy.lang.Closure)} instead
+     */
+    @Deprecated
     Task add(Map<String, ?> options, Closure configureClosure) throws InvalidUserDataException;
 
     /**
@@ -112,6 +168,22 @@ public interface TaskContainer extends TaskCollection<Task>, NamedDomainObjectCo
      * @return The newly created task object
      * @throws InvalidUserDataException If a task with the given name already exists in this project.
      */
+    Task create(String name, Closure configureClosure) throws InvalidUserDataException;
+
+    /**
+     * <p>Creates a {@link Task} with the given name adds it to this container. The given closure is used to configure
+     * the task before it is returned by this method.</p>
+     *
+     * <p>After the task is added, it is made available as a property of the project, so that you can reference the task
+     * by name in your build file. See <a href="../Project.html#properties">here</a> for more details.</p>
+     *
+     * @param name The name of the task to be created
+     * @param configureClosure The closure to use to configure the task.
+     * @return The newly created task object
+     * @throws InvalidUserDataException If a task with the given name already exists in this project.
+     * @deprecated use {@link #create(String, groovy.lang.Closure)} instead
+     */
+    @Deprecated
     Task add(String name, Closure configureClosure) throws InvalidUserDataException;
 
     /**
@@ -124,6 +196,20 @@ public interface TaskContainer extends TaskCollection<Task>, NamedDomainObjectCo
      * @return The newly created task object
      * @throws InvalidUserDataException If a task with the given name already exists in this project.
      */
+    Task create(String name) throws InvalidUserDataException;
+
+    /**
+     * <p>Creates a {@link Task} with the given name and adds it to this container.</p>
+     *
+     * <p>After the task is added, it is made available as a property of the project, so that you can reference the task
+     * by name in your build file. See <a href="../Project.html#properties">here</a> for more details.</p>
+     *
+     * @param name The name of the task to be created
+     * @return The newly created task object
+     * @throws InvalidUserDataException If a task with the given name already exists in this project.
+     * @deprecated use {@link #create(String)} instead
+     */
+    @Deprecated
     Task add(String name) throws InvalidUserDataException;
 
     /**
@@ -137,9 +223,38 @@ public interface TaskContainer extends TaskCollection<Task>, NamedDomainObjectCo
      * @return The newly created task object
      * @throws InvalidUserDataException If a task with the given name already exists in this project.
      */
+    <T extends Task> T create(String name, Class<T> type) throws InvalidUserDataException;
+
+    /**
+     * <p>Creates a {@link Task} with the given name and type, and adds it to this container.</p>
+     *
+     * <p>After the task is added, it is made available as a property of the project, so that you can reference the task
+     * by name in your build file. See <a href="../Project.html#properties">here</a> for more details.</p>
+     *
+     * @param name The name of the task to be created.
+     * @param type The type of task to create.
+     * @return The newly created task object
+     * @throws InvalidUserDataException If a task with the given name already exists in this project.
+     * @deprecated use {@link #create(String, Class)} instead
+     */
+    @Deprecated
     <T extends Task> T add(String name, Class<T> type) throws InvalidUserDataException;
 
     /**
+     * <p>Creates a {@link Task} with the given name and type, configures it with the given action, and adds it to this container.</p>
+     *
+     * <p>After the task is added, it is made available as a property of the project, so that you can reference the task
+     * by name in your build file. See <a href="../Project.html#properties">here</a> for more details.</p>
+     *
+     * @param name The name of the task to be created.
+     * @param type The type of task to create.
+     * @param configuration The action to configure the task with.
+     * @return The newly created task object.
+     * @throws InvalidUserDataException If a task with the given name already exists in this project.
+     */
+    <T extends Task> T create(String name, Class<T> type, Action<? super T> configuration) throws InvalidUserDataException;
+
+    /**
      * <p>Creates a {@link Task} with the given name and adds it to this container, replacing any existing task with the
      * same name.</p>
      *
diff --git a/subprojects/core/src/main/groovy/org/gradle/api/tasks/Upload.java b/subprojects/core/src/main/groovy/org/gradle/api/tasks/Upload.java
index 926975c..922186f 100644
--- a/subprojects/core/src/main/groovy/org/gradle/api/tasks/Upload.java
+++ b/subprojects/core/src/main/groovy/org/gradle/api/tasks/Upload.java
@@ -17,7 +17,6 @@
 package org.gradle.api.tasks;
 
 import groovy.lang.Closure;
-import org.apache.ivy.core.module.descriptor.ModuleDescriptor;
 import org.gradle.api.artifacts.Configuration;
 import org.gradle.api.artifacts.Module;
 import org.gradle.api.artifacts.PublishException;
@@ -28,8 +27,6 @@ import org.gradle.api.internal.Transformers;
 import org.gradle.api.internal.artifacts.ArtifactPublicationServices;
 import org.gradle.api.internal.artifacts.ArtifactPublisher;
 import org.gradle.api.internal.artifacts.configurations.ConfigurationInternal;
-import org.gradle.api.internal.artifacts.ivyservice.IvyModuleDescriptorWriter;
-import org.gradle.api.internal.artifacts.ivyservice.ModuleDescriptorConverter;
 import org.gradle.api.internal.artifacts.repositories.PublicationAwareRepository;
 import org.gradle.util.ConfigureUtil;
 
@@ -42,8 +39,6 @@ import static org.gradle.util.CollectionUtils.collect;
 
 /**
  * Uploads the artifacts of a {@link Configuration} to a set of repositories.
- *
- * @author Hans Dockter
  */
 public class Upload extends ConventionTask {
 
@@ -67,19 +62,11 @@ public class Upload extends ConventionTask {
         Set<Configuration> configurationsToPublish = configuration.getHierarchy();
 
         ArtifactPublisher artifactPublisher = publicationServices.createArtifactPublisher();
+        File descriptorDestination = isUploadDescriptor() ? getDescriptorDestination() : null;
+        List<PublicationAwareRepository> publishRepositories = collect(repositories, Transformers.cast(PublicationAwareRepository.class));
 
         try {
-            File descriptorDestination = isUploadDescriptor() ? getDescriptorDestination() : null;
-            if (descriptorDestination != null) {
-                Set<Configuration> allConfigurations = configurationsToPublish.iterator().next().getAll();
-                ModuleDescriptorConverter moduleDescriptorConverter = publicationServices.getDescriptorFileModuleConverter();
-                ModuleDescriptor moduleDescriptor = moduleDescriptorConverter.convert(allConfigurations, module);
-                IvyModuleDescriptorWriter ivyModuleDescriptorWriter = publicationServices.getIvyModuleDescriptorWriter();
-                ivyModuleDescriptorWriter.write(moduleDescriptor, descriptorDestination);
-            }
-
-            List<PublicationAwareRepository> publishRepositories = collect(repositories, Transformers.cast(PublicationAwareRepository.class));
-            artifactPublisher.publish(publishRepositories,  module, configurationsToPublish, descriptorDestination);
+            artifactPublisher.publish(publishRepositories, module, configurationsToPublish, descriptorDestination);
         } catch (Exception e) {
             throw new PublishException(String.format("Could not publish configuration '%s'", configuration.getName()), e);
         }
diff --git a/subprojects/core/src/main/groovy/org/gradle/api/tasks/bundling/AbstractArchiveTask.java b/subprojects/core/src/main/groovy/org/gradle/api/tasks/bundling/AbstractArchiveTask.java
index fd88ab9..3058b41 100644
--- a/subprojects/core/src/main/groovy/org/gradle/api/tasks/bundling/AbstractArchiveTask.java
+++ b/subprojects/core/src/main/groovy/org/gradle/api/tasks/bundling/AbstractArchiveTask.java
@@ -24,8 +24,6 @@ import java.io.File;
 
 /**
  * {@code AbstractArchiveTask} is the base class for all archive tasks.
- *
- * @author Hans Dockter
  */
 public abstract class AbstractArchiveTask extends AbstractCopyTask {
     private File destinationDir;
@@ -185,4 +183,5 @@ public abstract class AbstractArchiveTask extends AbstractCopyTask {
         super.into(destPath, configureClosure);
         return this;
     }
+
 }
diff --git a/subprojects/core/src/main/groovy/org/gradle/api/tasks/bundling/Compression.java b/subprojects/core/src/main/groovy/org/gradle/api/tasks/bundling/Compression.java
index f8b04db..863fe28 100644
--- a/subprojects/core/src/main/groovy/org/gradle/api/tasks/bundling/Compression.java
+++ b/subprojects/core/src/main/groovy/org/gradle/api/tasks/bundling/Compression.java
@@ -23,8 +23,6 @@ import java.util.List;
 
 /**
  * Specifies the compression which should be applied to a TAR archive.
- * 
- * @author Hans Dockter
  */
 public enum Compression {
     NONE("tar"),
diff --git a/subprojects/core/src/main/groovy/org/gradle/api/tasks/bundling/Tar.java b/subprojects/core/src/main/groovy/org/gradle/api/tasks/bundling/Tar.java
index f8af439..396cbef 100644
--- a/subprojects/core/src/main/groovy/org/gradle/api/tasks/bundling/Tar.java
+++ b/subprojects/core/src/main/groovy/org/gradle/api/tasks/bundling/Tar.java
@@ -16,29 +16,22 @@
 
 package org.gradle.api.tasks.bundling;
 
-import org.gradle.api.internal.file.FileResolver;
-import org.gradle.api.internal.file.archive.TarCopySpecVisitor;
-import org.gradle.api.internal.file.archive.compression.Bzip2Archiver;
+import org.gradle.api.internal.file.archive.TarCopyAction;
 import org.gradle.api.internal.file.archive.compression.ArchiveOutputStreamFactory;
+import org.gradle.api.internal.file.archive.compression.Bzip2Archiver;
 import org.gradle.api.internal.file.archive.compression.GzipArchiver;
 import org.gradle.api.internal.file.archive.compression.SimpleCompressor;
-import org.gradle.api.internal.file.copy.ArchiveCopyAction;
-import org.gradle.api.internal.file.copy.CopyActionImpl;
+import org.gradle.api.internal.file.copy.CopyAction;
 
-import java.io.File;
 import java.util.concurrent.Callable;
 
 /**
  * Assembles a TAR archive.
- *
- * @author Hans Dockter
  */
 public class Tar extends AbstractArchiveTask {
-    private final CopyActionImpl action;
     private Compression compression = Compression.NONE;
 
     public Tar() {
-        action = new TarCopyActionImpl(getServices().get(FileResolver.class));
         getConventionMapping().map("extension", new Callable<Object>(){
             public Object call() throws Exception {
                 return getCompression().getDefaultExtension();
@@ -46,10 +39,18 @@ public class Tar extends AbstractArchiveTask {
         });
     }
 
-    protected CopyActionImpl getCopyAction() {
-        return action;
+    @Override
+    protected CopyAction createCopyAction() {
+        return new TarCopyAction(getArchivePath(), getCompressor());
     }
 
+    private ArchiveOutputStreamFactory getCompressor() {
+        switch(compression) {
+            case BZIP2: return Bzip2Archiver.getCompressor();
+            case GZIP:  return GzipArchiver.getCompressor();
+            default:    return new SimpleCompressor();
+        }
+    }
     /**
      * Returns the compression that is used for this archive.
      *
@@ -68,21 +69,4 @@ public class Tar extends AbstractArchiveTask {
         this.compression = compression;
     }
 
-    private class TarCopyActionImpl extends CopyActionImpl implements ArchiveCopyAction  {
-        public TarCopyActionImpl(FileResolver fileResolver) {
-            super(fileResolver, new TarCopySpecVisitor());
-        }
-
-        public File getArchivePath() {
-            return Tar.this.getArchivePath();
-        }
-
-        public ArchiveOutputStreamFactory getCompressor() {
-            switch(compression) {
-                case BZIP2: return Bzip2Archiver.getCompressor();
-                case GZIP:  return GzipArchiver.getCompressor();
-                default:    return new SimpleCompressor();
-            }
-        }
-    }
 }
\ No newline at end of file
diff --git a/subprojects/core/src/main/groovy/org/gradle/api/tasks/bundling/Zip.java b/subprojects/core/src/main/groovy/org/gradle/api/tasks/bundling/Zip.java
index e7c515f..9b43610 100644
--- a/subprojects/core/src/main/groovy/org/gradle/api/tasks/bundling/Zip.java
+++ b/subprojects/core/src/main/groovy/org/gradle/api/tasks/bundling/Zip.java
@@ -15,31 +15,39 @@
  */
 package org.gradle.api.tasks.bundling;
 
-import org.gradle.api.internal.file.FileResolver;
 import org.gradle.api.internal.file.archive.ZipCopyAction;
-import org.gradle.api.internal.file.archive.ZipCopySpecVisitor;
-import org.gradle.api.internal.file.copy.CopyActionImpl;
-import org.gradle.api.internal.file.copy.ZipDeflatedCompressor;
+import org.gradle.api.internal.file.copy.CopyAction;
 import org.gradle.api.internal.file.copy.ZipCompressor;
+import org.gradle.api.internal.file.copy.ZipDeflatedCompressor;
 import org.gradle.api.internal.file.copy.ZipStoredCompressor;
 
-import java.io.File;
-
 /**
  * Assembles a ZIP archive.
  * 
  * The default is to compress the contents of the zip.
- * 
- * @author Hans Dockter
  */
 public class Zip extends AbstractArchiveTask {
     public static final String ZIP_EXTENSION = "zip";
-    private final ZipCopyActionImpl action;
     private ZipEntryCompression entryCompression = ZipEntryCompression.DEFLATED;
 
     public Zip() {
         setExtension(ZIP_EXTENSION);
-        action = new ZipCopyActionImpl(getServices().get(FileResolver.class));
+    }
+
+    protected ZipCompressor getCompressor() {
+        switch(entryCompression) {
+            case DEFLATED:
+                return ZipDeflatedCompressor.INSTANCE;
+            case STORED:
+                return ZipStoredCompressor.INSTANCE;
+            default:
+                throw new IllegalArgumentException(String.format("Unknown Compression type %s", entryCompression));
+        }
+    }
+
+    @Override
+    protected CopyAction createCopyAction() {
+        return new ZipCopyAction(getArchivePath(), getCompressor());
     }
 
     /**
@@ -62,31 +70,12 @@ public class Zip extends AbstractArchiveTask {
         this.entryCompression = entryCompression;
     }
 
-    protected ZipCopyActionImpl getCopyAction() {
-        return action;
-    }
-
     /**
-     * Zip compress action implementation.
+     * DO NOT REMOVE.
+     *
+     * Do not use - kept for binary compatibility.
      */
-    protected class ZipCopyActionImpl extends CopyActionImpl implements ZipCopyAction {
-        public ZipCopyActionImpl(FileResolver fileResolver) {
-            super(fileResolver, new ZipCopySpecVisitor());
-        }
-
-        public File getArchivePath() {
-            return Zip.this.getArchivePath();
-        }
-
-        public ZipCompressor getCompressor() {
-            switch(entryCompression) {
-                case DEFLATED:
-                    return ZipDeflatedCompressor.INSTANCE;
-                case STORED:
-                    return ZipStoredCompressor.INSTANCE;
-                default:
-                    throw new IllegalArgumentException(String.format("Unknown Compression type %s", entryCompression));
-            }
-        }
-    }
+    @SuppressWarnings("UnusedDeclaration")
+    @Deprecated
+    protected class ZipCopyActionImpl {}
 }
diff --git a/subprojects/core/src/main/groovy/org/gradle/api/tasks/incremental/IncrementalTaskInputs.java b/subprojects/core/src/main/groovy/org/gradle/api/tasks/incremental/IncrementalTaskInputs.java
new file mode 100644
index 0000000..fc97522
--- /dev/null
+++ b/subprojects/core/src/main/groovy/org/gradle/api/tasks/incremental/IncrementalTaskInputs.java
@@ -0,0 +1,122 @@
+/*
+ * Copyright 2013 the original author or authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF 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.incremental;
+
+import org.gradle.api.Action;
+import org.gradle.api.Incubating;
+import org.gradle.api.NonExtensible;
+
+/**
+ * Provides access to any input files that need to be processed by an incremental task.
+ * <p>
+ * An incremental task action is one that accepts a single {@link IncrementalTaskInputs} parameter.
+ * The task can then provide an action to execute for all input files that are out of date with respect to the previous execution of the task,
+ * and a separate action for all input files that have been removed since the previous execution.
+ *
+ * <pre autoTested="true">
+ * class IncrementalReverseTask extends DefaultTask {
+ *      @InputDirectory
+ *      def File inputDir
+ *
+ *      @OutputDirectory
+ *      def File outputDir
+ *
+ *      @TaskAction
+ *      void execute(IncrementalTaskInputs inputs) {
+ *          inputs.outOfDate { change ->
+ *              def targetFile = project.file("$outputDir/${change.file.name}")
+ *              targetFile.text = change.file.text.reverse()
+ *          }
+ *
+ *          inputs.removed { change ->
+ *              def targetFile = project.file("$outputDir/${change.file.name}")
+ *              if (targetFile.exists()) {
+ *                  targetFile.delete()
+ *              }
+ *          }
+ *      }
+ *  }
+ * </pre>
+ *
+ * <p>
+ * In the case where Gradle is unable to determine which input files need to be reprocessed, then all of the input files will be reported as {@link #outOfDate}.
+ * Cases where this occurs include:
+ * <ul>
+ *     <li>There is no history available from a previous execution.</li>
+ *     <li>An {@link org.gradle.api.tasks.TaskOutputs#upToDateWhen(groovy.lang.Closure)} criteria added to the task returns <code>false</code>.</li>
+ *     <li>An {@link org.gradle.api.tasks.Input} property has changed since the previous execution.</li>
+ *     <li>One or more output files have changed since the previous execution.</li>
+ * </ul>
+ *
+ * Note that this is a stateful API:
+ * <ul>
+ *     <li>{@link #outOfDate} and {@link #removed} can each only be executed a single time per {@link IncrementalTaskInputs} instance.</li>
+ *     <li>{@link #outOfDate} must be executed before {@link #removed} is called.</li>
+ * </ul>
+ */
+ at Incubating
+ at NonExtensible
+public interface IncrementalTaskInputs {
+    /**
+     * Indicates if it was possible for Gradle to determine which exactly which input files were out of date compared to a previous execution.
+     * This is <em>not</em> possible in the case of no previous execution, changed Input Properties, Output Files, etc.
+     * <p>
+     * When <code>true</code>:
+     * <ul>
+     *     <li>Any input file that has been added or modified since previous execution will be considered 'out-of-date' and reported to {@link #outOfDate}.</li>
+     *     <li>Any input files that has been removed since previous execution will be reported to {@link #removed}.</li>
+     * </ul>
+     * </p>
+     * <p>
+     * When <code>false</code>:
+     * <ul>
+     *     <li>Every input file will be considered to be 'out-of-date' and will be reported to {@link #outOfDate}.</li>
+     *     <li>No input files will be reported to {@link #removed}.</li>
+     * </ul>
+     * </p>
+     */
+    boolean isIncremental();
+
+    /**
+     * Executes the action for all of the input files that are out-of-date since the previous task execution. The action may also be supplied as a {@link groovy.lang.Closure}.
+     * <ul>
+     *     <li>When {@link #isIncremental()} == <code>true</code>, the action will be executed for any added or modified input file.</li>
+     *     <li>When {@link #isIncremental()} == <code>false</code>, the action will be executed for every input file for the task.</li>
+     * </ul>
+     * <p>
+     * This method may only be called a single time for a single {@link IncrementalTaskInputs} instance.
+     * </p>
+     * @throws IllegalStateException on second and subsequent invocations.
+     */
+    void outOfDate(Action<? super InputFileDetails> outOfDateAction);
+
+    /**
+     * Executes the action for all of the input files that were removed since the previous task execution. The action may also be supplied as a {@link groovy.lang.Closure}.
+     * <ul>
+     *     <li>When {@link #isIncremental()} == <code>true</code>, the action will be executed for any removed input file.</li>
+     *     <li>When {@link #isIncremental()} == <code>false</code>, the action will not be executed.</li>
+     * </ul>
+     * <p>
+     * This method may only be called a single time for a single {@link IncrementalTaskInputs} instance.
+     * </p><p>
+     * This method may only be called after {@link #outOfDate} has been called.
+     * </p>
+     * @throws IllegalStateException if invoked prior to {@link #outOfDate}, or if invoked more than once.
+     */
+    void removed(Action<? super InputFileDetails> removedAction);
+
+}
diff --git a/subprojects/core/src/main/groovy/org/gradle/api/tasks/incremental/InputFileDetails.java b/subprojects/core/src/main/groovy/org/gradle/api/tasks/incremental/InputFileDetails.java
new file mode 100644
index 0000000..bb59cd3
--- /dev/null
+++ b/subprojects/core/src/main/groovy/org/gradle/api/tasks/incremental/InputFileDetails.java
@@ -0,0 +1,50 @@
+/*
+ * Copyright 2013 the original author or authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF 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.incremental;
+
+import org.gradle.api.Incubating;
+
+import java.io.File;
+
+/**
+ * A change to an input file.
+ */
+ at Incubating
+public interface InputFileDetails {
+    /**
+     * Was the file added?
+     * @return true if the file was added since the last execution
+     */
+    boolean isAdded();
+
+    /**
+     * Was the file modified?
+     * @return if the file was modified
+     */
+    boolean isModified();
+
+    /**
+     * Was the file removed?
+     * @return true if the file was removed since the last execution
+     */
+    boolean isRemoved();
+
+    /**
+     * The input file, which may no longer exist.
+     * @return the input file
+     */
+    File getFile();
+}
diff --git a/subprojects/core/src/main/groovy/org/gradle/api/tasks/incremental/package-info.java b/subprojects/core/src/main/groovy/org/gradle/api/tasks/incremental/package-info.java
new file mode 100644
index 0000000..59d40ba
--- /dev/null
+++ b/subprojects/core/src/main/groovy/org/gradle/api/tasks/incremental/package-info.java
@@ -0,0 +1,20 @@
+/*
+ * Copyright 2013 the original author or authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+/**
+ * API classes for implementing incremental tasks.
+ */
+package org.gradle.api.tasks.incremental;
\ No newline at end of file
diff --git a/subprojects/core/src/main/groovy/org/gradle/api/tasks/util/PatternSet.java b/subprojects/core/src/main/groovy/org/gradle/api/tasks/util/PatternSet.java
index 5379eff..690ac50 100644
--- a/subprojects/core/src/main/groovy/org/gradle/api/tasks/util/PatternSet.java
+++ b/subprojects/core/src/main/groovy/org/gradle/api/tasks/util/PatternSet.java
@@ -25,6 +25,8 @@ import org.gradle.api.file.FileTreeElement;
 import org.gradle.api.file.RelativePath;
 import org.gradle.api.internal.file.RelativePathSpec;
 import org.gradle.api.internal.file.pattern.PatternMatcherFactory;
+import org.gradle.api.internal.notations.NotationParserBuilder;
+import org.gradle.api.internal.notations.api.NotationParser;
 import org.gradle.api.internal.notations.parsers.CharSequenceNotationParser;
 import org.gradle.api.specs.*;
 import org.gradle.api.tasks.AntBuilderAware;
@@ -162,7 +164,10 @@ public class PatternSet implements AntBuilderAware, PatternFilterable {
     }
 
     public PatternSet include(Iterable includes) {
-        CharSequenceNotationParser parser = new CharSequenceNotationParser();
+        NotationParser<String> parser = new NotationParserBuilder<String>()
+                .resultingType(String.class)
+                .parser(new CharSequenceNotationParser())
+                .toComposite();
         for (Object include : includes) {
             this.includes.add(parser.parseNotation(include));
         }
diff --git a/subprojects/core/src/main/groovy/org/gradle/cache/CacheValidator.java b/subprojects/core/src/main/groovy/org/gradle/cache/CacheValidator.java
index af53db8..0f6c743 100644
--- a/subprojects/core/src/main/groovy/org/gradle/cache/CacheValidator.java
+++ b/subprojects/core/src/main/groovy/org/gradle/cache/CacheValidator.java
@@ -18,7 +18,7 @@ package org.gradle.cache;
 
 /**
  * CacheValidator interface can be used for specify a particular cache validation logic.
- * */
+  */
 public interface CacheValidator {
     boolean isValid();
 
diff --git a/subprojects/core/src/main/groovy/org/gradle/cache/internal/DefaultCacheAccess.java b/subprojects/core/src/main/groovy/org/gradle/cache/internal/DefaultCacheAccess.java
index 9ce31a2..8a8b99c 100644
--- a/subprojects/core/src/main/groovy/org/gradle/cache/internal/DefaultCacheAccess.java
+++ b/subprojects/core/src/main/groovy/org/gradle/cache/internal/DefaultCacheAccess.java
@@ -16,28 +16,33 @@
 package org.gradle.cache.internal;
 
 import net.jcip.annotations.ThreadSafe;
+import org.gradle.api.logging.Logger;
+import org.gradle.api.logging.Logging;
 import org.gradle.cache.CacheAccess;
-import org.gradle.messaging.serialize.DefaultSerializer;
 import org.gradle.cache.PersistentIndexedCache;
 import org.gradle.cache.internal.btree.BTreePersistentIndexedCache;
+import org.gradle.cache.internal.cacheops.CacheAccessOperationsStack;
 import org.gradle.internal.Factories;
 import org.gradle.internal.Factory;
 import org.gradle.internal.UncheckedException;
+import org.gradle.messaging.serialize.DefaultSerializer;
 import org.gradle.messaging.serialize.Serializer;
 
 import java.io.File;
-import java.util.ArrayList;
 import java.util.HashSet;
-import java.util.List;
 import java.util.Set;
 import java.util.concurrent.locks.Condition;
 import java.util.concurrent.locks.Lock;
 import java.util.concurrent.locks.ReentrantLock;
 
 import static org.gradle.cache.internal.FileLockManager.LockMode.Exclusive;
+import static org.gradle.cache.internal.FileLockManager.LockMode.Shared;
 
 @ThreadSafe
 public class DefaultCacheAccess implements CacheAccess {
+
+    private final static Logger LOG = Logging.getLogger(DefaultCacheAccess.class);
+
     private final String cacheDiplayName;
     private final File lockFile;
     private final FileLockManager lockManager;
@@ -48,23 +53,24 @@ public class DefaultCacheAccess implements CacheAccess {
     private Thread owner;
     private FileLockManager.LockMode lockMode;
     private FileLock fileLock;
-    private final ThreadLocal<CacheOperationStack> operationStack = new ThreadLocal<CacheOperationStack>() {
-        @Override
-        protected CacheOperationStack initialValue() {
-            return new CacheOperationStack();
-        }
-    };
+    private boolean contended;
+    private final CacheAccessOperationsStack operations;
+    private int cacheClosedCount;
 
     public DefaultCacheAccess(String cacheDisplayName, File lockFile, FileLockManager lockManager) {
+        this(cacheDisplayName, lockFile, lockManager, new CacheAccessOperationsStack());
+    }
+
+    public DefaultCacheAccess(String cacheDisplayName, File lockFile, FileLockManager lockManager, CacheAccessOperationsStack operations) {
         this.cacheDiplayName = cacheDisplayName;
         this.lockFile = lockFile;
         this.lockManager = lockManager;
+        this.operations = operations;
     }
 
     /**
-     * Opens this cache access with the given lock mode. Calling this with {@link org.gradle.cache.internal.FileLockManager.LockMode#Exclusive} will
-     * lock the cache for exclusive access from all other threads (including those in this process and all other processes), until
-     * {@link #close()} is called.
+     * Opens this cache access with the given lock mode. Calling this with {@link org.gradle.cache.internal.FileLockManager.LockMode#Exclusive} will lock the cache for exclusive access from all other
+     * threads (including those in this process and all other processes), until {@link #close()} is called.
      */
     public void open(FileLockManager.LockMode lockMode) {
         lock.lock();
@@ -76,30 +82,43 @@ public class DefaultCacheAccess implements CacheAccess {
             if (lockMode == FileLockManager.LockMode.None) {
                 return;
             }
+            if (fileLock != null) {
+                throw new IllegalStateException("File lock " + lockFile + " is already open.");
+            }
             fileLock = lockManager.lock(lockFile, lockMode, cacheDiplayName);
             takeOwnership(String.format("Access %s", cacheDiplayName));
+            lockManager.allowContention(fileLock, whenContended());
         } finally {
             lock.unlock();
         }
     }
 
-    public void close() {
-        lock.lock();
+    private void closeFileLock() {
         try {
+            cacheClosedCount++;
             for (MultiProcessSafePersistentIndexedCache<?, ?> cache : caches) {
                 cache.close();
             }
-            operationStack.remove();
-            lockMode = null;
-            owner = null;
+            fileLock.close();
+        } finally {
+            fileLock = null;
+            contended = false;
+        }
+    }
+
+    public void close() {
+        lock.lock();
+        try {
+            operations.close();
             if (fileLock != null) {
-                try {
-                    fileLock.close();
-                } finally {
-                    fileLock = null;
-                }
+                closeFileLock();
+            }
+            if (cacheClosedCount != 1) {
+                LOG.debug("Cache {} was closed {} times.", cacheDiplayName, cacheClosedCount);
             }
         } finally {
+            lockMode = null;
+            owner = null;
             lock.unlock();
         }
     }
@@ -118,17 +137,21 @@ public class DefaultCacheAccess implements CacheAccess {
         }
 
         takeOwnership(operationDisplayName);
+        boolean wasStarted = false;
         try {
-            boolean wasStarted = onStartWork();
+            wasStarted = onStartWork();
+            return factory.create();
+        } finally {
+            lock.lock();
             try {
-                return factory.create();
-            } finally {
                 if (wasStarted) {
-                    onEndWork();
+                    onEndWork(operationDisplayName);
+                } else {
+                    releaseOwnership(operationDisplayName);
                 }
+            } finally {
+                lock.unlock();
             }
-        } finally {
-            releaseOwnership(operationDisplayName);
         }
     }
 
@@ -143,7 +166,7 @@ public class DefaultCacheAccess implements CacheAccess {
                 }
             }
             owner = Thread.currentThread();
-            operationStack.get().pushCacheAction(operationDisplayName);
+            operations.pushCacheAction(operationDisplayName);
         } finally {
             lock.unlock();
         }
@@ -152,8 +175,8 @@ public class DefaultCacheAccess implements CacheAccess {
     private void releaseOwnership(String operationDisplayName) {
         lock.lock();
         try {
-            operationStack.get().popCacheAction(operationDisplayName);
-            if (!operationStack.get().isInCacheAction()) {
+            operations.popCacheAction(operationDisplayName);
+            if (!operations.isInCacheAction()) {
                 owner = null;
                 condition.signalAll();
             }
@@ -163,18 +186,30 @@ public class DefaultCacheAccess implements CacheAccess {
     }
 
     public <T> T longRunningOperation(String operationDisplayName, Factory<? extends T> action) {
-        if (operationStack.get().isInLongRunningOperation()) {
-            operationStack.get().pushLongRunningOperation(operationDisplayName);
+        boolean isReentrant;
+        lock.lock();
+        try {
+            isReentrant = operations.maybeReentrantLongRunningOperation(operationDisplayName);
+        } finally {
+            lock.unlock();
+        }
+        if (isReentrant) {
             try {
                 return action.create();
             } finally {
-                operationStack.get().popLongRunningOperation(operationDisplayName);
+                popLongRunningOperation(operationDisplayName);
             }
         }
 
-        checkThreadIsOwner();
-        boolean wasEnded = onEndWork();
-        parkOwner(operationDisplayName);
+        lock.lock();
+        boolean wasEnded;
+        try {
+            checkThreadIsOwner();
+            wasEnded = onEndWork();
+            parkOwner(operationDisplayName);
+        } finally {
+            lock.unlock();
+        }
         try {
             return action.create();
         } finally {
@@ -185,6 +220,15 @@ public class DefaultCacheAccess implements CacheAccess {
         }
     }
 
+    private void popLongRunningOperation(String operationDisplayName) {
+        lock.lock();
+        try {
+            operations.popLongRunningOperation(operationDisplayName);
+        } finally {
+            lock.unlock();
+        }
+    }
+
     private void checkThreadIsOwner() {
         lock.lock();
         try {
@@ -205,7 +249,7 @@ public class DefaultCacheAccess implements CacheAccess {
             owner = null;
             condition.signalAll();
 
-            operationStack.get().pushLongRunningOperation(operationDisplayName);
+            operations.pushLongRunningOperation(operationDisplayName);
         } finally {
             lock.unlock();
         }
@@ -222,7 +266,7 @@ public class DefaultCacheAccess implements CacheAccess {
                 }
             }
             owner = Thread.currentThread();
-            operationStack.get().popLongRunningOperation(description);
+            popLongRunningOperation(description);
         } finally {
             lock.unlock();
         }
@@ -251,7 +295,7 @@ public class DefaultCacheAccess implements CacheAccess {
         try {
             caches.add(indexedCache);
             if (fileLock != null) {
-                indexedCache.onStartWork(operationStack.get().getDescription());
+                indexedCache.onStartWork(operations.getDescription());
             }
         } finally {
             lock.unlock();
@@ -267,11 +311,14 @@ public class DefaultCacheAccess implements CacheAccess {
         if (fileLock != null) {
             return false;
         }
+        fileLock = lockManager.lock(lockFile, Exclusive, cacheDiplayName, operations.getDescription());
 
-        fileLock = lockManager.lock(lockFile, Exclusive, cacheDiplayName, operationStack.get().getDescription());
         for (MultiProcessSafePersistentIndexedCache<?, ?> cache : caches) {
-            cache.onStartWork(operationStack.get().getDescription());
+            cache.onStartWork(operations.getDescription());
         }
+
+        lockManager.allowContention(fileLock, whenContended());
+
         return true;
     }
 
@@ -279,23 +326,25 @@ public class DefaultCacheAccess implements CacheAccess {
         if (fileLock == null) {
             return false;
         }
+        if (contended || fileLock.getMode() == Shared) {
+            closeFileLock();
+        }
+        return true;
+    }
 
+    private void onEndWork(String operationToRelease) {
         try {
-            for (MultiProcessSafePersistentIndexedCache<?, ?> cache : caches) {
-                cache.onEndWork();
-            }
-            fileLock.close();
+            onEndWork();
         } finally {
-            fileLock = null;
+            releaseOwnership(operationToRelease);
         }
-        return true;
     }
 
     private FileLock getLock() {
         lock.lock();
         try {
-            if (Thread.currentThread() != owner || fileLock == null) {
-                throw new IllegalStateException(String.format("The %s has not been locked.", cacheDiplayName));
+            if ((Thread.currentThread() != owner && owner != null) || fileLock == null) {
+                throw new IllegalStateException(String.format("The %s has not been locked for this thread. File lock: %s, owner: %s", cacheDiplayName, fileLock != null, owner));
             }
         } finally {
             lock.unlock();
@@ -317,61 +366,39 @@ public class DefaultCacheAccess implements CacheAccess {
         }
     }
 
-    private class CacheOperationStack {
-        private final List<CacheOperation> operations = new ArrayList<CacheOperation>();
-
-        public String getDescription() {
-            checkNotEmpty();
-            return operations.get(0).description;
-        }
-
-        public boolean isInLongRunningOperation() {
-            return !operations.isEmpty() && operations.get(0).longRunningOperation;
-        }
-
-        public void pushLongRunningOperation(String description) {
-            operations.add(0, new CacheOperation(description, true));
-        }
-
-        public void popLongRunningOperation(String description) {
-            pop(description, true);
-        }
-
-        public boolean isInCacheAction() {
-            return !operations.isEmpty() && !operations.get(0).longRunningOperation;
-        }
-
-        public void pushCacheAction(String description) {
-            operations.add(0, new CacheOperation(description, false));
-        }
-
-        public void popCacheAction(String description) {
-            pop(description, false);
-        }
-
-        private CacheOperation pop(String description, boolean longRunningOperation) {
-            checkNotEmpty();
-            CacheOperation operation = operations.remove(0);
-            if (operation.description.equals(description) && operation.longRunningOperation == longRunningOperation) {
-                return operation;
-            }
-            throw new IllegalStateException();
-        }
-
-        private void checkNotEmpty() {
-            if (operations.isEmpty()) {
-                throw new IllegalStateException();
+    Runnable whenContended() {
+        return new Runnable() {
+            public void run() {
+                lock.lock();
+                try {
+                    LOG.debug("Detected file lock contention of {} (fileLock={}, contended={}, owner={})", cacheDiplayName, fileLock != null, contended, owner);
+                    if (fileLock == null) {
+                        //the lock may have been closed
+                        return;
+                    }
+                    if (owner != null) {
+                        contended = true;
+                        return;
+                    }
+
+                    takeOwnership("Other process requested access to " + cacheDiplayName);
+                    try {
+                        closeFileLock();
+                    } finally {
+                        releaseOwnership("Other process requested access to " + cacheDiplayName);
+                    }
+                } finally {
+                    lock.unlock();
+                }
             }
-        }
+        };
     }
 
-    private class CacheOperation {
-        final String description;
-        final boolean longRunningOperation;
+    Thread getOwner() {
+        return owner;
+    }
 
-        private CacheOperation(String description, boolean longRunningOperation) {
-            this.description = description;
-            this.longRunningOperation = longRunningOperation;
-        }
+    FileAccess getFileAccess() {
+        return fileAccess;
     }
-}
+}
\ No newline at end of file
diff --git a/subprojects/core/src/main/groovy/org/gradle/cache/internal/DefaultFileLockManager.java b/subprojects/core/src/main/groovy/org/gradle/cache/internal/DefaultFileLockManager.java
index a328e8a..18fa3f6 100644
--- a/subprojects/core/src/main/groovy/org/gradle/cache/internal/DefaultFileLockManager.java
+++ b/subprojects/core/src/main/groovy/org/gradle/cache/internal/DefaultFileLockManager.java
@@ -15,11 +15,15 @@
  */
 package org.gradle.cache.internal;
 
+import org.gradle.api.logging.Logger;
+import org.gradle.api.logging.Logging;
+import org.gradle.cache.internal.locklistener.FileLockContentionHandler;
+import org.gradle.internal.CompositeStoppable;
 import org.gradle.internal.Factory;
-import org.gradle.internal.UncheckedException;
+import org.gradle.internal.Stoppable;
+import org.gradle.internal.id.IdGenerator;
+import org.gradle.internal.id.RandomLongIdGenerator;
 import org.gradle.util.GFileUtils;
-import org.slf4j.Logger;
-import org.slf4j.LoggerFactory;
 
 import java.io.EOFException;
 import java.io.File;
@@ -28,33 +32,46 @@ import java.io.RandomAccessFile;
 import java.util.Set;
 import java.util.concurrent.CopyOnWriteArraySet;
 
+import static org.gradle.internal.UncheckedException.throwAsUncheckedException;
+
 /**
  * Uses file system locks on a lock file per target file. Each lock file is made up of 2 regions:
  *
- * <ul> <li>State region: 1 byte version field, 1 byte clean flag.</li> <li>Owner information region: 1 byte version field, utf-8 encoded owner process id, utf-8 encoded owner operation display
- * name.</li> </ul>
+ * <ul>
+ *     <li>State region: 1 byte version field, 1 byte clean flag.</li>
+ *     <li>Owner information region: 1 byte version field, bunch of other fields, see the code below for more info</li>
+ * </ul>
  */
 public class DefaultFileLockManager implements FileLockManager {
-    private static final Logger LOGGER = LoggerFactory.getLogger(DefaultFileLockManager.class);
+    private static final Logger LOGGER = Logging.getLogger(DefaultFileLockManager.class);
     private static final int DEFAULT_LOCK_TIMEOUT = 60000;
     private static final byte STATE_REGION_PROTOCOL = 1;
     private static final int STATE_REGION_SIZE = 2;
     private static final int STATE_REGION_POS = 0;
-    private static final byte INFORMATION_REGION_PROTOCOL = 2;
+    private static final byte INFORMATION_REGION_PROTOCOL = 3;
     private static final int INFORMATION_REGION_POS = STATE_REGION_POS + STATE_REGION_SIZE;
-    public static final int INFORMATION_REGION_SIZE = 2048;
+    public static final int INFORMATION_REGION_SIZE = 2052;
     public static final int INFORMATION_REGION_DESCR_CHUNK_LIMIT = 340;
     private final Set<File> lockedFiles = new CopyOnWriteArraySet<File>();
     private final ProcessMetaDataProvider metaDataProvider;
     private final int lockTimeoutMs;
+    private final IdGenerator<Long> generator;
+    private FileLockContentionHandler fileLockContentionHandler;
+    private final long shortTimeoutMs = 10000;
+
+    public DefaultFileLockManager(ProcessMetaDataProvider metaDataProvider, FileLockContentionHandler fileLockContentionHandler) {
+        this(metaDataProvider, DEFAULT_LOCK_TIMEOUT, fileLockContentionHandler);
+    }
 
-    public DefaultFileLockManager(ProcessMetaDataProvider metaDataProvider) {
-        this(metaDataProvider, DEFAULT_LOCK_TIMEOUT);
+    public DefaultFileLockManager(ProcessMetaDataProvider metaDataProvider, int lockTimeoutMs, FileLockContentionHandler fileLockContentionHandler) {
+        this(metaDataProvider, lockTimeoutMs, fileLockContentionHandler, new RandomLongIdGenerator());
     }
 
-    public DefaultFileLockManager(ProcessMetaDataProvider metaDataProvider, int lockTimeoutMs) {
+    public DefaultFileLockManager(ProcessMetaDataProvider metaDataProvider, int lockTimeoutMs, FileLockContentionHandler fileLockContentionHandler, IdGenerator<Long> generator) {
         this.metaDataProvider = metaDataProvider;
         this.lockTimeoutMs = lockTimeoutMs;
+        this.fileLockContentionHandler = fileLockContentionHandler;
+        this.generator = generator;
     }
 
     public FileLock lock(File target, LockMode mode, String targetDisplayName) throws LockTimeoutException {
@@ -70,13 +87,25 @@ public class DefaultFileLockManager implements FileLockManager {
             throw new IllegalStateException(String.format("Cannot lock %s as it has already been locked by this process.", targetDisplayName));
         }
         try {
-            return new DefaultFileLock(canonicalTarget, mode, targetDisplayName, operationDisplayName);
+            int port = fileLockContentionHandler.reservePort();
+            return new DefaultFileLock(canonicalTarget, mode, targetDisplayName, operationDisplayName, port);
         } catch (Throwable t) {
             lockedFiles.remove(canonicalTarget);
-            throw UncheckedException.throwAsUncheckedException(t);
+            throw throwAsUncheckedException(t);
         }
     }
 
+    public void allowContention(FileLock fileLock, Runnable whenContended) {
+        fileLockContentionHandler.start(fileLock.getLockId(), whenContended);
+    }
+
+    private class OwnerInfo {
+        int port;
+        long lockId;
+        String pid;
+        String operation;
+    }
+
     private class DefaultFileLock extends AbstractFileAccess implements FileLock {
         private final File lockFile;
         private final File target;
@@ -86,8 +115,12 @@ public class DefaultFileLockManager implements FileLockManager {
         private java.nio.channels.FileLock lock;
         private RandomAccessFile lockFileAccess;
         private boolean integrityViolated;
+        private int port;
+        private final long lockId;
 
-        public DefaultFileLock(File target, LockMode mode, String displayName, String operationDisplayName) throws Throwable {
+        public DefaultFileLock(File target, LockMode mode, String displayName, String operationDisplayName, int port) throws Throwable {
+            this.port = port;
+            this.lockId = generator.generateId();
             if (mode == LockMode.None) {
                 throw new UnsupportedOperationException("Locking mode None is not supported.");
             }
@@ -133,7 +166,7 @@ public class DefaultFileLockManager implements FileLockManager {
                 // Process has crashed writing to lock file
                 return false;
             } catch (Exception e) {
-                throw UncheckedException.throwAsUncheckedException(e);
+                throw throwAsUncheckedException(e);
             }
 
             return true;
@@ -166,7 +199,7 @@ public class DefaultFileLockManager implements FileLockManager {
                 markClean();
                 integrityViolated = false;
             } catch (Throwable t) {
-                throw UncheckedException.throwAsUncheckedException(t);
+                throw throwAsUncheckedException(t);
             }
         }
 
@@ -198,65 +231,76 @@ public class DefaultFileLockManager implements FileLockManager {
         }
 
         public void close() {
-            if (lockFileAccess == null) {
-                return;
-            }
-            try {
-                LOGGER.debug("Releasing lock on {}.", displayName);
-                lockedFiles.remove(target);
-                // Also releases any locks
-                try {
-                    if (lock != null && !lock.isShared()) {
-                        // Discard information region
-                        lockFileAccess.setLength(INFORMATION_REGION_POS);
+            CompositeStoppable stoppable = new CompositeStoppable();
+            stoppable.add(new Stoppable() {
+                public void stop() {
+                    try {
+                        fileLockContentionHandler.stop(lockId);
+                    } catch (Exception e) {
+                        throw new RuntimeException("Unable to stop listening for file lock requests for " + displayName, e);
                     }
-                } finally {
-                    lockFileAccess.close();
                 }
-            } catch (IOException e) {
-                LOGGER.warn("Error releasing lock on {}: {}", displayName, e);
-            } finally {
-                lock = null;
-                lockFileAccess = null;
-            }
+            });
+            stoppable.add(new Stoppable() {
+                public void stop() {
+                    if (lockFileAccess == null) {
+                        return;
+                    }
+                    try {
+                        LOGGER.debug("Releasing lock on {}.", displayName);
+                        try {
+                            if (lock != null && !lock.isShared()) {
+                                // Discard information region
+                                java.nio.channels.FileLock info;
+                                try {
+                                    info = lockInformationRegion(LockMode.Exclusive, System.currentTimeMillis() + shortTimeoutMs);
+                                } catch (InterruptedException e) {
+                                    throw throwAsUncheckedException(e);
+                                }
+                                if (info != null) {
+                                    try {
+                                        lockFileAccess.setLength(INFORMATION_REGION_POS);
+                                    } finally {
+                                        info.release();
+                                    }
+                                }
+                            }
+                        } finally {
+                            lockFileAccess.close();
+                        }
+                    } catch (Exception e) {
+                        throw new RuntimeException("Failed to release lock on " + displayName, e);
+                    }
+                }
+            });
+            stoppable.add(new Stoppable() {
+                public void stop() {
+                    lock = null;
+                    lockFileAccess = null;
+                    lockedFiles.remove(target);
+                }
+            });
+            stoppable.stop();
         }
 
         public LockMode getMode() {
             return mode;
         }
 
+        public long getLockId() {
+            return lockId;
+        }
+
         private java.nio.channels.FileLock lock(FileLockManager.LockMode lockMode) throws Throwable {
             LOGGER.debug("Waiting to acquire {} lock on {}.", lockMode.toString().toLowerCase(), displayName);
-            long timeout = System.currentTimeMillis() + lockTimeoutMs;
+            long waitUntil = System.currentTimeMillis() + lockTimeoutMs;
 
             // Lock the state region, with the requested mode
-            java.nio.channels.FileLock stateRegionLock = lockStateRegion(lockMode, timeout);
+            java.nio.channels.FileLock stateRegionLock = lockStateRegion(lockMode, waitUntil);
             if (stateRegionLock == null) {
-                // Can't acquire lock, get details of owner to include in the error message
-                String ownerPid = "unknown";
-                String ownerOperation = "unknown";
-                java.nio.channels.FileLock informationRegionLock = lockInformationRegion(LockMode.Shared, timeout);
-                if (informationRegionLock == null) {
-                    LOGGER.debug("Could not lock information region for {}. Ignoring.", displayName);
-                } else {
-                    try {
-                        if (lockFileAccess.length() <= INFORMATION_REGION_POS) {
-                            LOGGER.debug("Lock file for {} is too short to contain information region. Ignoring.", displayName);
-                        } else {
-                            lockFileAccess.seek(INFORMATION_REGION_POS);
-                            if (lockFileAccess.readByte() != INFORMATION_REGION_PROTOCOL) {
-                                throw new IllegalStateException(String.format("Unexpected lock protocol found in lock file '%s' for %s.", lockFile, displayName));
-                            }
-                            ownerPid = lockFileAccess.readUTF();
-                            ownerOperation = lockFileAccess.readUTF();
-                        }
-                    } finally {
-                        informationRegionLock.release();
-                    }
-                }
-
+                OwnerInfo ownerInfo = readInformationRegion(System.currentTimeMillis() + shortTimeoutMs);
                 throw new LockTimeoutException(String.format("Timeout waiting to lock %s. It is currently in use by another Gradle instance.%nOwner PID: %s%nOur PID: %s%nOwner Operation: %s%nOur operation: %s%nLock file: %s",
-                        displayName, ownerPid, metaDataProvider.getProcessIdentifier(), ownerOperation, operationDisplayName, lockFile));
+                        displayName, ownerInfo.pid, metaDataProvider.getProcessIdentifier(), ownerInfo.operation, operationDisplayName, lockFile));
             }
 
             try {
@@ -277,14 +321,16 @@ public class DefaultFileLockManager implements FileLockManager {
                         lockFileAccess.writeBoolean(false);
                     }
                     // Acquire an exclusive lock on the information region and write our details there
-                    java.nio.channels.FileLock informationRegionLock = lockInformationRegion(LockMode.Exclusive, timeout);
+                    java.nio.channels.FileLock informationRegionLock = lockInformationRegion(LockMode.Exclusive, System.currentTimeMillis() + shortTimeoutMs);
                     if (informationRegionLock == null) {
-                        throw new IllegalStateException(String.format("Timeout waiting to lock the information region for lock %s", displayName));
+                        throw new IllegalStateException(String.format("Unable to lock the information region for %s", displayName));
                     }
                     // check that the length of the reserved region is enough for storing our content
                     try {
                         lockFileAccess.seek(INFORMATION_REGION_POS);
                         lockFileAccess.writeByte(INFORMATION_REGION_PROTOCOL);
+                        lockFileAccess.writeInt(port);
+                        lockFileAccess.writeLong(lockId);
                         lockFileAccess.writeUTF(trimIfNecessary(metaDataProvider.getProcessIdentifier()));
                         lockFileAccess.writeUTF(trimIfNecessary(operationDisplayName));
                         lockFileAccess.setLength(lockFileAccess.getFilePointer());
@@ -301,31 +347,81 @@ public class DefaultFileLockManager implements FileLockManager {
             return stateRegionLock;
         }
 
+        private OwnerInfo readInformationRegion(long waitUntil) throws IOException, InterruptedException {
+            // Can't acquire lock, get details of owner to include in the error message
+            OwnerInfo out = new OwnerInfo();
+            out.pid = "unknown";
+            out.operation = "unknown";
+            out.port = -1;
+            java.nio.channels.FileLock informationRegionLock = lockInformationRegion(LockMode.Shared, waitUntil);
+            if (informationRegionLock == null) {
+                LOGGER.debug("Could not lock information region for {}. Ignoring.", displayName);
+            } else {
+                try {
+                    if (lockFileAccess.length() <= INFORMATION_REGION_POS) {
+                        LOGGER.debug("Lock file for {} is too short to contain information region. Ignoring.", displayName);
+                    } else {
+                        lockFileAccess.seek(INFORMATION_REGION_POS);
+                        if (lockFileAccess.readByte() != INFORMATION_REGION_PROTOCOL) {
+                            throw new IllegalStateException(String.format("Unexpected lock protocol found in lock file '%s' for %s.", lockFile, displayName));
+                        }
+                        out.port = lockFileAccess.readInt();
+                        out.lockId = lockFileAccess.readLong();
+                        out.pid = lockFileAccess.readUTF();
+                        out.operation = lockFileAccess.readUTF();
+                        LOGGER.debug("Read following information from the file lock info region. Port: {}, owner: {}, operation: {}", out.port, out.pid, out.operation);
+                    }
+                } finally {
+                    informationRegionLock.release();
+                }
+            }
+            return out;
+        }
+
         private String trimIfNecessary(String inputString) {
             if(inputString.length() > INFORMATION_REGION_DESCR_CHUNK_LIMIT){
                 return inputString.substring(0, INFORMATION_REGION_DESCR_CHUNK_LIMIT);
-            }else{
+            } else {
                 return inputString;
             }
         }
 
-        private java.nio.channels.FileLock lockStateRegion(LockMode lockMode, long timeout) throws IOException, InterruptedException {
-            return lockRegion(lockMode, timeout, STATE_REGION_POS, STATE_REGION_SIZE);
-        }
-
-        private java.nio.channels.FileLock lockInformationRegion(LockMode lockMode, long timeout) throws IOException, InterruptedException {
-            return lockRegion(lockMode, timeout, INFORMATION_REGION_POS, INFORMATION_REGION_SIZE - INFORMATION_REGION_POS);
+        private java.nio.channels.FileLock lockStateRegion(LockMode lockMode, final long waitUntil) throws IOException, InterruptedException {
+            do {
+                java.nio.channels.FileLock fileLock = lockRegion(lockMode, STATE_REGION_POS, STATE_REGION_SIZE);
+                if (fileLock != null) {
+                    return fileLock;
+                }
+                if (port != -1) { //we don't like the assumption about the port very much
+                    OwnerInfo ownerInfo = readInformationRegion(System.currentTimeMillis()); //no need for timeout here, as we're already looping with timeout
+                    if (ownerInfo.port != -1) {
+                        LOGGER.debug("The file lock is held by a different Gradle process (pid: {}, operation: {}). Will attempt to ping owner at port {}", ownerInfo.pid, ownerInfo.operation, ownerInfo.port);
+                        fileLockContentionHandler.pingOwner(ownerInfo.port, ownerInfo.lockId, displayName);
+                    } else {
+                        LOGGER.debug("The file lock is held by a different Gradle process. I was unable to read on which port the owner listens for lock access requests.");
+                    }
+                }
+                //TODO SF it would really nice to print some message to the user after say 2 seconds of waiting
+                //saying what gradle is doing, and why we're waiting.
+                Thread.sleep(200L);
+            } while (System.currentTimeMillis() < waitUntil);
+            return null;
         }
 
-        private java.nio.channels.FileLock lockRegion(FileLockManager.LockMode lockMode, long timeout, long start, long size) throws IOException, InterruptedException {
+        private java.nio.channels.FileLock lockInformationRegion(LockMode lockMode, long waitUntil) throws IOException, InterruptedException {
             do {
-                java.nio.channels.FileLock fileLock = lockFileAccess.getChannel().tryLock(start, size, lockMode == LockMode.Shared);
+                java.nio.channels.FileLock fileLock = lockRegion(lockMode, INFORMATION_REGION_POS, INFORMATION_REGION_SIZE - INFORMATION_REGION_POS);
                 if (fileLock != null) {
                     return fileLock;
                 }
                 Thread.sleep(200L);
-            } while (System.currentTimeMillis() < timeout);
+            }
+            while (System.currentTimeMillis() < waitUntil);
             return null;
         }
+
+        private java.nio.channels.FileLock lockRegion(LockMode lockMode, long start, long size) throws IOException, InterruptedException {
+            return lockFileAccess.getChannel().tryLock(start, size, lockMode == LockMode.Shared);
+        }
     }
 }
\ No newline at end of file
diff --git a/subprojects/core/src/main/groovy/org/gradle/cache/internal/FileAccess.java b/subprojects/core/src/main/groovy/org/gradle/cache/internal/FileAccess.java
index 892941b..cbfb4e2 100644
--- a/subprojects/core/src/main/groovy/org/gradle/cache/internal/FileAccess.java
+++ b/subprojects/core/src/main/groovy/org/gradle/cache/internal/FileAccess.java
@@ -54,7 +54,7 @@ public interface FileAccess {
     void updateFile(Runnable action) throws LockTimeoutException, FileIntegrityViolationException, InsufficientLockModeException;
 
     /**
-     * Runs the given action under an exclusive lock on the target file, without checking it's integrity. If the given action fails, the lock is marked as uncleanly unlocked.
+     * Runs the given action under an exclusive lock on the target file, without checking its integrity. If the given action fails, the lock is marked as uncleanly unlocked.
      *
      * <p>This method should be used when it is of no consequence if the target was not previously unlocked, e.g. the content is being replaced.
      *
diff --git a/subprojects/core/src/main/groovy/org/gradle/cache/internal/FileLock.java b/subprojects/core/src/main/groovy/org/gradle/cache/internal/FileLock.java
index 04896e6..7345741 100644
--- a/subprojects/core/src/main/groovy/org/gradle/cache/internal/FileLock.java
+++ b/subprojects/core/src/main/groovy/org/gradle/cache/internal/FileLock.java
@@ -41,4 +41,9 @@ public interface FileLock extends Closeable, FileAccess {
      * The actual mode of the lock. May be different to what was requested.
      */
     FileLockManager.LockMode getMode();
+
+    /**
+     * @return unique id of this lock
+     */
+    long getLockId();
 }
diff --git a/subprojects/core/src/main/groovy/org/gradle/cache/internal/FileLockCommunicator.java b/subprojects/core/src/main/groovy/org/gradle/cache/internal/FileLockCommunicator.java
new file mode 100644
index 0000000..b78974d
--- /dev/null
+++ b/subprojects/core/src/main/groovy/org/gradle/cache/internal/FileLockCommunicator.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.cache.internal;
+
+import org.gradle.messaging.remote.internal.inet.InetAddressFactory;
+
+import java.io.*;
+import java.net.DatagramPacket;
+import java.net.DatagramSocket;
+import java.net.InetAddress;
+import java.net.SocketException;
+
+import static org.gradle.internal.UncheckedException.throwAsUncheckedException;
+
+public class FileLockCommunicator {
+    private final DatagramSocket socket;
+    private final InetAddressFactory addressFactory;
+    private boolean stopped;
+
+    public FileLockCommunicator(InetAddressFactory addressFactory) {
+        this.addressFactory = addressFactory;
+        try {
+            socket = new DatagramSocket();
+        } catch (SocketException e) {
+            throw throwAsUncheckedException(e);
+        }
+    }
+
+    public void pingOwner(int ownerPort, long lockId, String displayName) {
+        try {
+            byte[] bytesToSend = encode(lockId);
+            // Ping the owner via all available local addresses
+            for (InetAddress address : addressFactory.findLocalAddresses()) {
+                socket.send(new DatagramPacket(bytesToSend, bytesToSend.length, address, ownerPort));
+            }
+        } catch (IOException e) {
+            throw new RuntimeException(String.format("Failed to ping owner of lock for %s (lock id: %s, port: %s)", displayName, lockId, ownerPort), e);
+        }
+    }
+
+    public long receive() throws GracefullyStoppedException {
+        try {
+            byte[] bytes = new byte[8];
+            DatagramPacket packet = new DatagramPacket(bytes, bytes.length);
+            socket.receive(packet);
+            return decode(bytes);
+        } catch (IOException e) {
+            if (!stopped) {
+                throw new RuntimeException(e);
+            }
+            throw new GracefullyStoppedException();
+        }
+    }
+
+    public void stop() {
+        stopped = true;
+        socket.close();
+    }
+
+    private static byte[] encode(long lockId) throws IOException {
+        ByteArrayOutputStream packet = new ByteArrayOutputStream();
+        new DataOutputStream(packet).writeLong(lockId);
+        return packet.toByteArray();
+    }
+
+    private static long decode(byte[] bytes) throws IOException {
+        return new DataInputStream(new ByteArrayInputStream(bytes)).readLong();
+    }
+
+    public int getPort() {
+        return socket.getLocalPort();
+    }
+}
\ No newline at end of file
diff --git a/subprojects/core/src/main/groovy/org/gradle/cache/internal/FileLockManager.java b/subprojects/core/src/main/groovy/org/gradle/cache/internal/FileLockManager.java
index fab7db9..8d6738c 100644
--- a/subprojects/core/src/main/groovy/org/gradle/cache/internal/FileLockManager.java
+++ b/subprojects/core/src/main/groovy/org/gradle/cache/internal/FileLockManager.java
@@ -39,6 +39,15 @@ public interface FileLockManager {
      */
     FileLock lock(File target, LockMode mode, String targetDisplayName, String operationDisplayName) throws LockTimeoutException;
 
+    /**
+     * Enables other processes to request access to the provided lock. Provided action runs when the lock access request is received
+     * (it means that the lock is contended).
+     *
+     * @param fileLock the lock
+     * @param whenContended will be called asynchronously by the thread that listens for cache access requests, when such request is received
+     */
+    void allowContention(FileLock fileLock, Runnable whenContended);
+
     enum LockMode {
         /**
          * No synchronisation is done.
diff --git a/subprojects/core/src/main/groovy/org/gradle/cache/internal/GracefullyStoppedException.java b/subprojects/core/src/main/groovy/org/gradle/cache/internal/GracefullyStoppedException.java
new file mode 100644
index 0000000..35f2b19
--- /dev/null
+++ b/subprojects/core/src/main/groovy/org/gradle/cache/internal/GracefullyStoppedException.java
@@ -0,0 +1,19 @@
+/*
+ * Copyright 2011 the original author or authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.gradle.cache.internal;
+
+public class GracefullyStoppedException extends Exception {
+}
diff --git a/subprojects/core/src/main/groovy/org/gradle/cache/internal/MultiProcessSafePersistentIndexedCache.java b/subprojects/core/src/main/groovy/org/gradle/cache/internal/MultiProcessSafePersistentIndexedCache.java
index 8006d0e..8bfcd55 100644
--- a/subprojects/core/src/main/groovy/org/gradle/cache/internal/MultiProcessSafePersistentIndexedCache.java
+++ b/subprojects/core/src/main/groovy/org/gradle/cache/internal/MultiProcessSafePersistentIndexedCache.java
@@ -15,9 +15,9 @@
  */
 package org.gradle.cache.internal;
 
-import org.gradle.internal.Factory;
 import org.gradle.cache.PersistentIndexedCache;
 import org.gradle.cache.internal.btree.BTreePersistentIndexedCache;
+import org.gradle.internal.Factory;
 
 import java.io.Closeable;
 
diff --git a/subprojects/core/src/main/groovy/org/gradle/cache/internal/OnDemandFileAccess.java b/subprojects/core/src/main/groovy/org/gradle/cache/internal/OnDemandFileAccess.java
index 1e70c09..8f999f4 100644
--- a/subprojects/core/src/main/groovy/org/gradle/cache/internal/OnDemandFileAccess.java
+++ b/subprojects/core/src/main/groovy/org/gradle/cache/internal/OnDemandFileAccess.java
@@ -39,6 +39,10 @@ public class OnDemandFileAccess extends AbstractFileAccess {
         }
     }
 
+    private Runnable doNothing() {
+        return new Runnable() { public void run() {} };
+    }
+
     public void updateFile(Runnable action) throws LockTimeoutException, FileIntegrityViolationException {
         FileLock lock = manager.lock(targetFile, FileLockManager.LockMode.Exclusive, displayName);
         try {
diff --git a/subprojects/core/src/main/groovy/org/gradle/cache/internal/SimpleStateCache.java b/subprojects/core/src/main/groovy/org/gradle/cache/internal/SimpleStateCache.java
index fcd415d..370b61e 100644
--- a/subprojects/core/src/main/groovy/org/gradle/cache/internal/SimpleStateCache.java
+++ b/subprojects/core/src/main/groovy/org/gradle/cache/internal/SimpleStateCache.java
@@ -19,6 +19,8 @@ package org.gradle.cache.internal;
 import org.gradle.api.GradleException;
 import org.gradle.internal.Factory;
 import org.gradle.cache.PersistentStateCache;
+import org.gradle.messaging.serialize.InputStreamBackedDecoder;
+import org.gradle.messaging.serialize.OutputStreamBackedEncoder;
 import org.gradle.messaging.serialize.Serializer;
 
 import java.io.*;
@@ -62,11 +64,11 @@ public class SimpleStateCache<T> implements PersistentStateCache<T> {
 
     private void serialize(T newValue) {
         try {
-            OutputStream outStr = new BufferedOutputStream(new FileOutputStream(cacheFile));
+            OutputStreamBackedEncoder encoder = new OutputStreamBackedEncoder(new BufferedOutputStream(new FileOutputStream(cacheFile)));
             try {
-                serializer.write(outStr, newValue);
+                serializer.write(encoder, newValue);
             } finally {
-                outStr.close();
+                encoder.close();
             }
         } catch (Exception e) {
             throw new GradleException(String.format("Could not write cache value to '%s'.", cacheFile), e);
@@ -78,11 +80,11 @@ public class SimpleStateCache<T> implements PersistentStateCache<T> {
             return null;
         }
         try {
-            InputStream inStr = new BufferedInputStream(new FileInputStream(cacheFile));
+            InputStreamBackedDecoder decoder = new InputStreamBackedDecoder(new BufferedInputStream(new FileInputStream(cacheFile)));
             try {
-                return serializer.read(inStr);
+                return serializer.read(decoder);
             } finally {
-                inStr.close();
+                decoder.close();
             }
         } catch (Exception e) {
             throw new GradleException(String.format("Could not read cache value from '%s'.", cacheFile), e);
diff --git a/subprojects/core/src/main/groovy/org/gradle/cache/internal/btree/BTreePersistentIndexedCache.java b/subprojects/core/src/main/groovy/org/gradle/cache/internal/btree/BTreePersistentIndexedCache.java
index 061f283..f84dc47 100644
--- a/subprojects/core/src/main/groovy/org/gradle/cache/internal/btree/BTreePersistentIndexedCache.java
+++ b/subprojects/core/src/main/groovy/org/gradle/cache/internal/btree/BTreePersistentIndexedCache.java
@@ -17,6 +17,8 @@ package org.gradle.cache.internal.btree;
 
 import org.gradle.api.UncheckedIOException;
 import org.gradle.cache.PersistentIndexedCache;
+import org.gradle.messaging.serialize.InputStreamBackedDecoder;
+import org.gradle.messaging.serialize.OutputStreamBackedEncoder;
 import org.gradle.messaging.serialize.Serializer;
 import org.slf4j.Logger;
 import org.slf4j.LoggerFactory;
@@ -131,7 +133,9 @@ public class BTreePersistentIndexedCache<K, V> implements PersistentIndexedCache
     public void put(K key, V value) {
         try {
             MessageDigestStream digestStream = new MessageDigestStream();
-            keySerializer.write(digestStream, key);
+            OutputStreamBackedEncoder encoder = new OutputStreamBackedEncoder(digestStream);
+            keySerializer.write(encoder, key);
+            encoder.flush();
             long hashCode = digestStream.getChecksum();
             Lookup lookup = header.getRoot().find(hashCode);
             boolean needNewBlock = true;
@@ -447,7 +451,9 @@ public class BTreePersistentIndexedCache<K, V> implements PersistentIndexedCache
 
         public Lookup find(K key) throws Exception {
             MessageDigestStream digestStream = new MessageDigestStream();
-            keySerializer.write(digestStream, key);
+            OutputStreamBackedEncoder encoder = new OutputStreamBackedEncoder(digestStream);
+            keySerializer.write(encoder, key);
+            encoder.flush();
             long checksum = digestStream.getChecksum();
             return find(checksum);
         }
@@ -645,13 +651,15 @@ public class BTreePersistentIndexedCache<K, V> implements PersistentIndexedCache
 
         public void setValue(V value) throws Exception {
             ByteArrayOutputStream outStr = new ByteArrayOutputStream();
-            serializer.write(outStr, value);
+            OutputStreamBackedEncoder encoder = new OutputStreamBackedEncoder(outStr);
+            serializer.write(encoder, value);
+            encoder.flush();
             this.serialisedValue = outStr.toByteArray();
         }
 
         public V getValue() throws Exception {
             if (value == null) {
-                value = serializer.read(new ByteArrayInputStream(serialisedValue));
+                value = serializer.read(new InputStreamBackedDecoder(new ByteArrayInputStream(serialisedValue)));
             }
             return value;
         }
diff --git a/subprojects/core/src/main/groovy/org/gradle/cache/internal/cacheops/CacheAccessOperationsStack.java b/subprojects/core/src/main/groovy/org/gradle/cache/internal/cacheops/CacheAccessOperationsStack.java
new file mode 100644
index 0000000..938e3c4
--- /dev/null
+++ b/subprojects/core/src/main/groovy/org/gradle/cache/internal/cacheops/CacheAccessOperationsStack.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.cache.internal.cacheops;
+
+import java.util.HashMap;
+import java.util.Map;
+
+import static java.lang.Thread.currentThread;
+
+public class CacheAccessOperationsStack {
+
+    private final Map<Thread, CacheOperationStack> perThreadStacks = new HashMap<Thread, CacheOperationStack>();
+
+    public void close() {
+        perThreadStacks.remove(currentThread());
+    }
+
+    public void pushCacheAction(String operationDisplayName) {
+        if (perThreadStacks.containsKey(currentThread())) {
+            getCurrentStack().pushCacheAction(operationDisplayName);
+        } else {
+            perThreadStacks.put(currentThread(), new CacheOperationStack().pushCacheAction(operationDisplayName));
+        }
+    }
+
+    public void popCacheAction(String operationDisplayName) {
+        getCurrentStack().popCacheAction(operationDisplayName);
+    }
+
+    public boolean isInCacheAction() {
+        return perThreadStacks.containsKey(currentThread()) && getCurrentStack().isInCacheAction();
+    }
+
+    public void pushLongRunningOperation(String operationDisplayName) {
+        if (perThreadStacks.containsKey(currentThread())) {
+            getCurrentStack().pushLongRunningOperation(operationDisplayName);
+        } else {
+            perThreadStacks.put(currentThread(), new CacheOperationStack().pushLongRunningOperation(operationDisplayName));
+        }
+    }
+
+    public void popLongRunningOperation(String operationDisplayName) {
+        getCurrentStack().popLongRunningOperation(operationDisplayName);
+    }
+
+    public String getDescription() {
+        return getCurrentStack().getDescription();
+    }
+
+    private CacheOperationStack getCurrentStack() {
+        if (!perThreadStacks.containsKey(currentThread())) {
+            throw new IllegalStateException("operations stack not ready. Was push action invoked?");
+        }
+        return perThreadStacks.get(currentThread());
+    }
+
+    public boolean maybeReentrantLongRunningOperation(String operationDisplayName) {
+        boolean atLeastOneLongRunning = false;
+        for (Thread thread : perThreadStacks.keySet()) {
+            if(perThreadStacks.get(thread).isInCacheAction()) {
+                //if any operation is in cache it means we're in cache operation and it isn't a reentrant long running operation
+                return false;
+            }
+            if (perThreadStacks.get(thread).isInLongRunningOperation()) {
+                atLeastOneLongRunning = true;
+            }
+        }
+
+        if (atLeastOneLongRunning) {
+            pushLongRunningOperation(operationDisplayName);
+            return true;
+        } else {
+            return false;
+        }
+    }
+}
\ No newline at end of file
diff --git a/subprojects/core/src/main/groovy/org/gradle/cache/internal/cacheops/CacheOperation.java b/subprojects/core/src/main/groovy/org/gradle/cache/internal/cacheops/CacheOperation.java
new file mode 100644
index 0000000..4df99a9
--- /dev/null
+++ b/subprojects/core/src/main/groovy/org/gradle/cache/internal/cacheops/CacheOperation.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.cache.internal.cacheops;
+
+class CacheOperation {
+    final String description;
+    final boolean longRunningOperation;
+
+    CacheOperation(String description, boolean longRunningOperation) {
+        this.description = description;
+        this.longRunningOperation = longRunningOperation;
+    }
+}
diff --git a/subprojects/core/src/main/groovy/org/gradle/cache/internal/cacheops/CacheOperationStack.java b/subprojects/core/src/main/groovy/org/gradle/cache/internal/cacheops/CacheOperationStack.java
new file mode 100644
index 0000000..52f5522
--- /dev/null
+++ b/subprojects/core/src/main/groovy/org/gradle/cache/internal/cacheops/CacheOperationStack.java
@@ -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.cache.internal.cacheops;
+
+import java.util.ArrayList;
+import java.util.List;
+
+class CacheOperationStack {
+    private final List<CacheOperation> operations = new ArrayList<CacheOperation>();
+
+    public String getDescription() {
+        checkNotEmpty();
+        return operations.get(0).description;
+    }
+
+    public CacheOperationStack pushLongRunningOperation(String description) {
+        operations.add(0, new CacheOperation(description, true));
+        return this;
+    }
+
+    public void popLongRunningOperation(String description) {
+        pop(description, true);
+    }
+
+    public boolean isInCacheAction() {
+        return !operations.isEmpty() && !operations.get(0).longRunningOperation;
+    }
+
+    public boolean isInLongRunningOperation() {
+        return !operations.isEmpty() && !isInCacheAction();
+    }
+
+    public CacheOperationStack pushCacheAction(String description) {
+        operations.add(0, new CacheOperation(description, false));
+        return this;
+    }
+
+    public void popCacheAction(String description) {
+        pop(description, false);
+    }
+
+    private CacheOperation pop(String description, boolean longRunningOperation) {
+        checkNotEmpty();
+        CacheOperation operation = operations.remove(0);
+        if (operation.description.equals(description) && operation.longRunningOperation == longRunningOperation) {
+            return operation;
+        }
+        throw new IllegalStateException("Cannot pop operation '" + description + "'. It is not at the top of the stack");
+    }
+
+    private void checkNotEmpty() {
+        if (operations.isEmpty()) {
+            throw new IllegalStateException();
+        }
+    }
+}
diff --git a/subprojects/core/src/main/groovy/org/gradle/cache/internal/locklistener/DefaultFileLockContentionHandler.java b/subprojects/core/src/main/groovy/org/gradle/cache/internal/locklistener/DefaultFileLockContentionHandler.java
new file mode 100644
index 0000000..89e3b7f
--- /dev/null
+++ b/subprojects/core/src/main/groovy/org/gradle/cache/internal/locklistener/DefaultFileLockContentionHandler.java
@@ -0,0 +1,160 @@
+/*
+ * Copyright 2013 the original author or authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.gradle.cache.internal.locklistener;
+
+import org.gradle.api.logging.Logger;
+import org.gradle.api.logging.Logging;
+import org.gradle.cache.internal.FileLockCommunicator;
+import org.gradle.cache.internal.GracefullyStoppedException;
+import org.gradle.internal.concurrent.ExecutorFactory;
+import org.gradle.internal.concurrent.StoppableExecutor;
+import org.gradle.messaging.remote.internal.inet.InetAddressFactory;
+
+import java.util.HashMap;
+import java.util.Map;
+import java.util.concurrent.locks.Lock;
+import java.util.concurrent.locks.ReentrantLock;
+
+public class DefaultFileLockContentionHandler implements FileLockContentionHandler {
+    private static final Logger LOGGER = Logging.getLogger(DefaultFileLockContentionHandler.class);
+    private final Lock lock = new ReentrantLock();
+    private final Map<Long, Runnable> contendedActions = new HashMap<Long, Runnable>();
+    private final ExecutorFactory executorFactory;
+    private final InetAddressFactory addressFactory;
+
+    private FileLockCommunicator communicator;
+    private StoppableExecutor executor;
+    private boolean stopped;
+
+    public DefaultFileLockContentionHandler(ExecutorFactory executorFactory, InetAddressFactory addressFactory) {
+        this.executorFactory = executorFactory;
+        this.addressFactory = addressFactory;
+    }
+
+    private Runnable listener() {
+        return new Runnable() {
+            public void run() {
+                try {
+                    LOGGER.info("Starting file lock listener thread.");
+                    doRun();
+                } catch (Throwable t) {
+                    //Logging exception here is only needed because by default Gradle does not show the stack trace
+                    LOGGER.error("Problems handling incoming cache access requests.", t);
+                } finally {
+                    LOGGER.info("File lock listener thread completed.");
+                }
+            }
+
+            private void doRun() {
+                while (true) {
+                    long lockId;
+                    try {
+                        lockId = communicator.receive();
+                    } catch (GracefullyStoppedException e) {
+                        return;
+                    }
+                    lock.lock();
+                    Runnable action;
+                    try {
+                        action = contendedActions.get(lockId);
+                        if (action == null) {
+                            //received access request for lock that is already closed
+                            continue;
+                        }
+                    } finally {
+                        lock.unlock();
+                    }
+                    action.run();
+                }
+            }
+        };
+    }
+
+    public void start(long lockId, Runnable whenContended) {
+        lock.lock();
+        try {
+            assertNotStopped();
+            if (communicator == null) {
+                throw new IllegalStateException("Must initialize the handler by reserving the port first.");
+            }
+            if (executor == null) {
+                executor = executorFactory.create("File lock request listener");
+                executor.execute(listener());
+            }
+            contendedActions.put(lockId, whenContended);
+        } finally {
+            lock.unlock();
+        }
+    }
+
+    public void pingOwner(int port, long lockId, String displayName) {
+        getCommunicator().pingOwner(port, lockId, displayName);
+    }
+
+    private void assertNotStopped() {
+        if (stopped) {
+            throw new IllegalStateException(
+                    "Cannot start managing file contention because this handler has been closed.");
+        }
+    }
+
+    public void stop(long lockId) {
+        lock.lock();
+        try {
+            contendedActions.remove(lockId);
+        } finally {
+            lock.unlock();
+        }
+    }
+
+    public void     stop() {
+        //Down the road this method should be used to clean up,
+        //when the Gradle process is about to complete (not gradle build).
+        //Ideally in future, this is happens during the clean-up/stopping of the global services
+        // (at the moment we never stop the global services)
+        lock.lock();
+        try {
+            stopped = true;
+            contendedActions.clear();
+            if (communicator != null) {
+                communicator.stop();
+            }
+        } finally {
+            lock.unlock();
+        }
+        if (executor != null) {
+            executor.stop();
+        }
+    }
+
+    public int reservePort() {
+        return getCommunicator().getPort();
+    }
+
+    private FileLockCommunicator getCommunicator() {
+        lock.lock();
+        try {
+            assertNotStopped();
+            if (communicator == null) {
+                communicator = new FileLockCommunicator(addressFactory);
+            }
+            return communicator;
+        } finally {
+            lock.unlock();
+        }
+    }
+}
diff --git a/subprojects/core/src/main/groovy/org/gradle/cache/internal/locklistener/FileLockContentionHandler.java b/subprojects/core/src/main/groovy/org/gradle/cache/internal/locklistener/FileLockContentionHandler.java
new file mode 100644
index 0000000..28a37a0
--- /dev/null
+++ b/subprojects/core/src/main/groovy/org/gradle/cache/internal/locklistener/FileLockContentionHandler.java
@@ -0,0 +1,27 @@
+/*
+ * Copyright 2013 the original author or authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.gradle.cache.internal.locklistener;
+
+public interface FileLockContentionHandler {
+    void start(long lockId, Runnable whenContended);
+
+    void stop(long lockId);
+
+    int reservePort();
+
+    void pingOwner(int port, long lockId, String displayName);
+}
diff --git a/subprojects/core/src/main/groovy/org/gradle/cache/internal/locklistener/NoOpFileLockContentionHandler.java b/subprojects/core/src/main/groovy/org/gradle/cache/internal/locklistener/NoOpFileLockContentionHandler.java
new file mode 100644
index 0000000..8d81bb7
--- /dev/null
+++ b/subprojects/core/src/main/groovy/org/gradle/cache/internal/locklistener/NoOpFileLockContentionHandler.java
@@ -0,0 +1,31 @@
+/*
+ * Copyright 2013 the original author or authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.gradle.cache.internal.locklistener;
+
+public class NoOpFileLockContentionHandler implements FileLockContentionHandler {
+
+    public void start(long lockId, Runnable whenContended) {}
+
+    public void stop(long lockId) {}
+
+    public int reservePort() {
+        return -1;
+    }
+
+    public void pingOwner(int port, long lockId, String displayName) {
+    }
+}
diff --git a/subprojects/core/src/main/groovy/org/gradle/configuration/BuildScriptProcessor.java b/subprojects/core/src/main/groovy/org/gradle/configuration/BuildScriptProcessor.java
deleted file mode 100644
index 9d7ffcc..0000000
--- a/subprojects/core/src/main/groovy/org/gradle/configuration/BuildScriptProcessor.java
+++ /dev/null
@@ -1,45 +0,0 @@
-/*
- * Copyright 2010 the original author or authors.
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- *      http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-package org.gradle.configuration;
-
-import org.gradle.api.internal.project.ProjectInternal;
-import org.gradle.api.internal.project.ProjectStateInternal;
-import org.gradle.util.Clock;
-import org.slf4j.Logger;
-import org.slf4j.LoggerFactory;
-
-public class BuildScriptProcessor implements ProjectEvaluator {
-    private static final Logger LOGGER = LoggerFactory.getLogger(BuildScriptProcessor.class);
-    private final ScriptPluginFactory configurerFactory;
-
-    public BuildScriptProcessor(ScriptPluginFactory configurerFactory) {
-        this.configurerFactory = configurerFactory;
-    }
-
-    public void evaluate(ProjectInternal project, ProjectStateInternal state) {
-        LOGGER.info(String.format("Evaluating %s using %s.", project, project.getBuildScriptSource().getDisplayName()));
-        Clock clock = new Clock();
-
-        try {
-            ScriptPlugin configurer = configurerFactory.create(project.getBuildScriptSource());
-            configurer.apply(project);
-        } catch (Exception e) {
-            state.executed(e);
-        }
-
-        LOGGER.debug("Timing: Running the build script took " + clock.getTime());
-    }
-}
diff --git a/subprojects/core/src/main/groovy/org/gradle/configuration/DefaultBuildConfigurer.java b/subprojects/core/src/main/groovy/org/gradle/configuration/DefaultBuildConfigurer.java
index a6e02fa..d14c7eb 100644
--- a/subprojects/core/src/main/groovy/org/gradle/configuration/DefaultBuildConfigurer.java
+++ b/subprojects/core/src/main/groovy/org/gradle/configuration/DefaultBuildConfigurer.java
@@ -15,27 +15,31 @@
  */
 package org.gradle.configuration;
 
-import org.gradle.api.Action;
+import org.gradle.StartParameter;
+import org.gradle.api.Project;
 import org.gradle.api.internal.GradleInternal;
 import org.gradle.api.internal.project.ProjectInternal;
 import org.gradle.util.SingleMessageLogger;
 
 public class DefaultBuildConfigurer implements BuildConfigurer {
-
     public void configure(GradleInternal gradle) {
-        gradle.addProjectEvaluationListener(new ImplicitTasksConfigurer());
-        gradle.addProjectEvaluationListener(new ProjectDependencies2TaskResolver());
+        maybeInformAboutIncubatingMode(gradle.getStartParameter());
         if (gradle.getStartParameter().isConfigureOnDemand()) {
-            SingleMessageLogger.informAboutIncubating("Configuration on demand");
             gradle.getRootProject().evaluate();
         } else {
-            gradle.getRootProject().allprojects((Action) new ConfigureProject());
+            for (Project project : gradle.getRootProject().getAllprojects()) {
+                ((ProjectInternal) project).evaluate();
+            }
         }
     }
 
-    static class ConfigureProject implements Action<ProjectInternal> {
-        public void execute(ProjectInternal projectInternal) {
-            projectInternal.evaluate();
+    private void maybeInformAboutIncubatingMode(StartParameter startParameter) {
+        if (startParameter.getParallelThreadCount() != 0 && startParameter.isConfigureOnDemand()) {
+            SingleMessageLogger.incubatingFeatureUsed("Parallel execution with configuration on demand");
+        } else if (startParameter.getParallelThreadCount() != 0) {
+            SingleMessageLogger.incubatingFeatureUsed("Parallel execution");
+        } else if (startParameter.isConfigureOnDemand()) {
+            SingleMessageLogger.incubatingFeatureUsed("Configuration on demand");
         }
     }
 }
\ No newline at end of file
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 8ee6e7c..4acd25b 100755
--- a/subprojects/core/src/main/groovy/org/gradle/configuration/DefaultScriptPluginFactory.java
+++ b/subprojects/core/src/main/groovy/org/gradle/configuration/DefaultScriptPluginFactory.java
@@ -16,14 +16,15 @@
 
 package org.gradle.configuration;
 
-import org.gradle.internal.Factory;
-import org.gradle.internal.service.DefaultServiceRegistry;
-import org.gradle.groovy.scripts.internal.BuildScriptClasspathScriptTransformer;
-import org.gradle.groovy.scripts.internal.BuildScriptTransformer;
 import org.gradle.api.internal.initialization.ScriptClassLoaderProvider;
 import org.gradle.api.internal.initialization.ScriptHandlerFactory;
 import org.gradle.api.internal.initialization.ScriptHandlerInternal;
 import org.gradle.groovy.scripts.*;
+import org.gradle.groovy.scripts.internal.BuildScriptClasspathScriptTransformer;
+import org.gradle.groovy.scripts.internal.BuildScriptTransformer;
+import org.gradle.internal.Factory;
+import org.gradle.internal.reflect.Instantiator;
+import org.gradle.internal.service.DefaultServiceRegistry;
 import org.gradle.logging.LoggingManagerInternal;
 
 public class DefaultScriptPluginFactory implements ScriptPluginFactory {
@@ -32,17 +33,20 @@ public class DefaultScriptPluginFactory implements ScriptPluginFactory {
     private final ScriptHandlerFactory scriptHandlerFactory;
     private final ClassLoader defaultClassLoader;
     private final Factory<LoggingManagerInternal> loggingManagerFactory;
+    private final Instantiator instantiator;
 
     public DefaultScriptPluginFactory(ScriptCompilerFactory scriptCompilerFactory,
-                                                ImportsReader importsReader,
-                                                ScriptHandlerFactory scriptHandlerFactory,
-                                                ClassLoader defaultClassLoader,
-                                                Factory<LoggingManagerInternal> loggingManagerFactory) {
+                                      ImportsReader importsReader,
+                                      ScriptHandlerFactory scriptHandlerFactory,
+                                      ClassLoader defaultClassLoader,
+                                      Factory<LoggingManagerInternal> loggingManagerFactory,
+                                      Instantiator instantiator) {
         this.scriptCompilerFactory = scriptCompilerFactory;
         this.importsReader = importsReader;
         this.scriptHandlerFactory = scriptHandlerFactory;
         this.defaultClassLoader = defaultClassLoader;
         this.loggingManagerFactory = loggingManagerFactory;
+        this.instantiator = instantiator;
     }
 
     public ScriptPlugin create(ScriptSource scriptSource) {
@@ -88,6 +92,7 @@ public class DefaultScriptPluginFactory implements ScriptPluginFactory {
             DefaultServiceRegistry services = new DefaultServiceRegistry();
             services.add(ScriptPluginFactory.class, DefaultScriptPluginFactory.this);
             services.add(LoggingManagerInternal.class, loggingManagerFactory.create());
+            services.add(Instantiator.class, instantiator);
 
             ScriptAware scriptAware = null;
             if (target instanceof ScriptAware) {
diff --git a/subprojects/core/src/main/groovy/org/gradle/configuration/ImplicitTasksConfigurer.java b/subprojects/core/src/main/groovy/org/gradle/configuration/ImplicitTasksConfigurer.java
index cab2353..600d78f 100644
--- a/subprojects/core/src/main/groovy/org/gradle/configuration/ImplicitTasksConfigurer.java
+++ b/subprojects/core/src/main/groovy/org/gradle/configuration/ImplicitTasksConfigurer.java
@@ -15,12 +15,7 @@
  */
 package org.gradle.configuration;
 
-import org.gradle.api.Project;
-import org.gradle.api.ProjectEvaluationListener;
-import org.gradle.api.ProjectState;
-
-//This one should go away once we complete the auto-apply plugins
-public class ImplicitTasksConfigurer implements ProjectEvaluationListener {
+public class ImplicitTasksConfigurer {
     public static final String HELP_GROUP = "help";
     public static final String HELP_TASK = "help";
     public static final String PROJECTS_TASK = "projects";
@@ -28,10 +23,4 @@ public class ImplicitTasksConfigurer implements ProjectEvaluationListener {
     public static final String PROPERTIES_TASK = "properties";
     public static final String DEPENDENCIES_TASK = "dependencies";
     public static final String DEPENDENCY_INSIGHT_TASK = "dependencyInsight";
-
-    public void beforeEvaluate(Project project) {}
-
-    public void afterEvaluate(Project project, ProjectState state) {
-        project.getPlugins().apply("help-tasks");
-    }
 }
diff --git a/subprojects/core/src/main/groovy/org/gradle/configuration/ImportsReader.java b/subprojects/core/src/main/groovy/org/gradle/configuration/ImportsReader.java
index 2ed96f4..b01f074 100644
--- a/subprojects/core/src/main/groovy/org/gradle/configuration/ImportsReader.java
+++ b/subprojects/core/src/main/groovy/org/gradle/configuration/ImportsReader.java
@@ -19,13 +19,10 @@ package org.gradle.configuration;
 import org.gradle.groovy.scripts.ScriptSource;
 import org.gradle.internal.UncheckedException;
 
-import java.net.URL;
-import java.io.InputStreamReader;
 import java.io.IOException;
+import java.io.InputStreamReader;
+import java.net.URL;
 
-/**
- * @author Hans Dockter
- */
 public class ImportsReader {
 
     private String importsText;
@@ -33,20 +30,23 @@ public class ImportsReader {
     public String getImports() {
         if (importsText == null) {
             try {
-                URL url = getClass().getResource("default-imports.txt");
+                URL url = getClass().getResource("/default-imports.txt");
                 InputStreamReader reader = new InputStreamReader(url.openStream(), "UTF8");
-
-                int bufferSize = 2048; // at time of writing, the file was about 1k so this should cover in one read
-                StringBuilder imports = new StringBuilder(bufferSize);
-                char[] chars = new char[bufferSize];
-
-                int numRead = reader.read(chars, 0, bufferSize);
-                while (numRead != -1) {
-                    imports.append(chars, 0, numRead);
-                    numRead = reader.read(chars, 0, bufferSize);
+                try {
+                    int bufferSize = 2048; // at time of writing, the file was about 1k so this should cover in one read
+                    StringBuilder imports = new StringBuilder(bufferSize);
+                    char[] chars = new char[bufferSize];
+
+                    int numRead = reader.read(chars, 0, bufferSize);
+                    while (numRead != -1) {
+                        imports.append(chars, 0, numRead);
+                        numRead = reader.read(chars, 0, bufferSize);
+                    }
+
+                    importsText = imports.toString();
+                } finally {
+                    reader.close();
                 }
-
-                importsText = imports.toString();
             } catch (IOException e) {
                 throw UncheckedException.throwAsUncheckedException(e);
             }
diff --git a/subprojects/core/src/main/groovy/org/gradle/configuration/LifecycleProjectEvaluator.java b/subprojects/core/src/main/groovy/org/gradle/configuration/LifecycleProjectEvaluator.java
deleted file mode 100644
index 5d0ee20..0000000
--- a/subprojects/core/src/main/groovy/org/gradle/configuration/LifecycleProjectEvaluator.java
+++ /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.configuration;
-
-import org.gradle.api.ProjectEvaluationListener;
-import org.gradle.api.internal.project.ProjectInternal;
-import org.gradle.api.internal.project.ProjectStateInternal;
-
-/**
- * Manages lifecycle concerns while delegating actual evaluation to another evaluator
- * 
- * @see org.gradle.api.internal.project.TopLevelBuildServiceRegistry#createProjectEvaluator()
- */
-public class LifecycleProjectEvaluator implements ProjectEvaluator {
-    private final ProjectEvaluator evaluator;
-
-    public LifecycleProjectEvaluator(ProjectEvaluator evaluator) {
-        this.evaluator = evaluator;
-    }
-
-    public void evaluate(ProjectInternal project, ProjectStateInternal state) {
-        //TODO this is one of the places to look into thread safety when we implement parallel configuration
-        if (state.getExecuted() || state.getExecuting()) {
-            return;
-        }
-
-        ProjectEvaluationListener listener = project.getProjectEvaluationBroadcaster();
-        listener.beforeEvaluate(project);
-        state.setExecuting(true);
-        try {
-            evaluator.evaluate(project, state);
-        } finally {
-            state.setExecuting(false);
-            state.executed();
-            listener.afterEvaluate(project, state);
-        }
-    }
-}
diff --git a/subprojects/core/src/main/groovy/org/gradle/configuration/ProjectDependencies2TaskResolver.java b/subprojects/core/src/main/groovy/org/gradle/configuration/ProjectDependencies2TaskResolver.java
deleted file mode 100644
index 9ab5cd5..0000000
--- a/subprojects/core/src/main/groovy/org/gradle/configuration/ProjectDependencies2TaskResolver.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.configuration;
-
-import org.gradle.api.Project;
-import org.gradle.api.ProjectEvaluationListener;
-import org.gradle.api.ProjectState;
-import org.gradle.api.Task;
-import org.slf4j.Logger;
-import org.slf4j.LoggerFactory;
-
-/**
- * @author Hans Dockter
- */
-public class ProjectDependencies2TaskResolver implements ProjectEvaluationListener {
-    private static Logger logger = LoggerFactory.getLogger(ProjectDependencies2TaskResolver.class);
-
-    public void beforeEvaluate(Project project) {}
-
-    public void afterEvaluate(Project project, ProjectState state) {
-        for (Project dependsOnProject : project.getDependsOnProjects()) {
-            logger.debug("Checking task dependencies for project: {} dependsOn: {}", project, dependsOnProject);
-            for (Task task : project.getTasks()) {
-                String taskName = task.getName();
-                Task dependentTask = dependsOnProject.getTasks().findByName(taskName);
-                if (dependentTask != null) {
-                    logger.debug("Setting task dependencies for task: {}", taskName);
-                    task.dependsOn(dependentTask);
-                }
-            }
-        }
-    }
-}
\ No newline at end of file
diff --git a/subprojects/core/src/main/groovy/org/gradle/configuration/ProjectEvaluator.java b/subprojects/core/src/main/groovy/org/gradle/configuration/ProjectEvaluator.java
deleted file mode 100644
index 677cc61..0000000
--- a/subprojects/core/src/main/groovy/org/gradle/configuration/ProjectEvaluator.java
+++ /dev/null
@@ -1,23 +0,0 @@
-/*
- * Copyright 2010 the original author or authors.
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- *      http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-package org.gradle.configuration;
-
-import org.gradle.api.internal.project.ProjectInternal;
-import org.gradle.api.internal.project.ProjectStateInternal;
-
-public interface ProjectEvaluator {
-    void evaluate(ProjectInternal project, ProjectStateInternal state);
-}
diff --git a/subprojects/core/src/main/groovy/org/gradle/configuration/project/BuildScriptProcessor.java b/subprojects/core/src/main/groovy/org/gradle/configuration/project/BuildScriptProcessor.java
new file mode 100644
index 0000000..93b4cf2
--- /dev/null
+++ b/subprojects/core/src/main/groovy/org/gradle/configuration/project/BuildScriptProcessor.java
@@ -0,0 +1,43 @@
+/*
+ * Copyright 2010 the original author or authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.gradle.configuration.project;
+
+import org.gradle.api.internal.project.ProjectInternal;
+import org.gradle.configuration.ScriptPlugin;
+import org.gradle.configuration.ScriptPluginFactory;
+import org.gradle.util.Clock;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+public class BuildScriptProcessor implements ProjectConfigureAction {
+    private static final Logger LOGGER = LoggerFactory.getLogger(BuildScriptProcessor.class);
+    private final ScriptPluginFactory configurerFactory;
+
+    public BuildScriptProcessor(ScriptPluginFactory configurerFactory) {
+        this.configurerFactory = configurerFactory;
+    }
+
+    public void execute(ProjectInternal project) {
+        LOGGER.info(String.format("Evaluating %s using %s.", project, project.getBuildScriptSource().getDisplayName()));
+        Clock clock = new Clock();
+        try {
+            ScriptPlugin configurer = configurerFactory.create(project.getBuildScriptSource());
+            configurer.apply(project);
+        } finally {
+            LOGGER.debug("Timing: Running the build script took " + clock.getTime());
+        }
+    }
+}
diff --git a/subprojects/core/src/main/groovy/org/gradle/configuration/project/ConfigureActionsProjectEvaluator.java b/subprojects/core/src/main/groovy/org/gradle/configuration/project/ConfigureActionsProjectEvaluator.java
new file mode 100644
index 0000000..e4d4c3e
--- /dev/null
+++ b/subprojects/core/src/main/groovy/org/gradle/configuration/project/ConfigureActionsProjectEvaluator.java
@@ -0,0 +1,37 @@
+/*
+ * Copyright 2013 the original author or authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.gradle.configuration.project;
+
+import org.gradle.api.internal.project.ProjectInternal;
+import org.gradle.api.internal.project.ProjectStateInternal;
+
+import java.util.Arrays;
+import java.util.List;
+
+public class ConfigureActionsProjectEvaluator implements ProjectEvaluator {
+    private final List<ProjectConfigureAction> configureActions;
+
+    public ConfigureActionsProjectEvaluator(ProjectConfigureAction... configureActions) {
+        this.configureActions = Arrays.asList(configureActions);
+    }
+
+    public void evaluate(ProjectInternal project, ProjectStateInternal state) {
+        for (ProjectConfigureAction configureAction : configureActions) {
+            configureAction.execute(project);
+        }
+    }
+}
diff --git a/subprojects/core/src/main/groovy/org/gradle/configuration/project/DefaultProjectConfigurationActionContainer.java b/subprojects/core/src/main/groovy/org/gradle/configuration/project/DefaultProjectConfigurationActionContainer.java
new file mode 100644
index 0000000..03e7879
--- /dev/null
+++ b/subprojects/core/src/main/groovy/org/gradle/configuration/project/DefaultProjectConfigurationActionContainer.java
@@ -0,0 +1,45 @@
+/*
+ * Copyright 2013 the original author or authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.gradle.configuration.project;
+
+import groovy.lang.Closure;
+import org.gradle.api.Action;
+import org.gradle.api.internal.ClosureBackedAction;
+import org.gradle.api.internal.project.ProjectInternal;
+
+import java.util.ArrayList;
+import java.util.List;
+
+public class DefaultProjectConfigurationActionContainer implements ProjectConfigurationActionContainer {
+    private final List<Action<? super ProjectInternal>> actions = new ArrayList<Action<? super ProjectInternal>>();
+
+    public void finished() {
+        actions.clear();
+    }
+
+    public List<Action<? super ProjectInternal>> getActions() {
+        return actions;
+    }
+
+    public void add(Action<? super ProjectInternal> action) {
+        actions.add(action);
+    }
+
+    public void add(Closure action) {
+        add(new ClosureBackedAction<ProjectInternal>(action));
+    }
+}
diff --git a/subprojects/core/src/main/groovy/org/gradle/configuration/project/DelayedConfigurationActions.java b/subprojects/core/src/main/groovy/org/gradle/configuration/project/DelayedConfigurationActions.java
new file mode 100644
index 0000000..9af016c
--- /dev/null
+++ b/subprojects/core/src/main/groovy/org/gradle/configuration/project/DelayedConfigurationActions.java
@@ -0,0 +1,33 @@
+/*
+ * Copyright 2013 the original author or authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.gradle.configuration.project;
+
+import org.gradle.api.Action;
+import org.gradle.api.internal.project.ProjectInternal;
+
+public class DelayedConfigurationActions implements ProjectConfigureAction {
+    public void execute(ProjectInternal projectInternal) {
+        ProjectConfigurationActionContainer actions = projectInternal.getConfigurationActions();
+        try {
+            for (Action<? super ProjectInternal> action : actions.getActions()) {
+                action.execute(projectInternal);
+            }
+        } finally {
+            actions.finished();
+        }
+    }
+}
diff --git a/subprojects/core/src/main/groovy/org/gradle/configuration/project/LifecycleProjectEvaluator.java b/subprojects/core/src/main/groovy/org/gradle/configuration/project/LifecycleProjectEvaluator.java
new file mode 100644
index 0000000..1a1547a
--- /dev/null
+++ b/subprojects/core/src/main/groovy/org/gradle/configuration/project/LifecycleProjectEvaluator.java
@@ -0,0 +1,82 @@
+/*
+ * Copyright 2010 the original author or authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.gradle.configuration.project;
+
+import org.gradle.api.ProjectConfigurationException;
+import org.gradle.api.ProjectEvaluationListener;
+import org.gradle.api.internal.project.ProjectInternal;
+import org.gradle.api.internal.project.ProjectStateInternal;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+/**
+ * Manages lifecycle concerns while delegating actual evaluation to another evaluator
+ * 
+ * @see org.gradle.internal.service.scopes.BuildScopeServices#createProjectEvaluator()
+ */
+public class LifecycleProjectEvaluator implements ProjectEvaluator {
+    private static final Logger LOGGER = LoggerFactory.getLogger(LifecycleProjectEvaluator.class);
+
+    private final ProjectEvaluator delegate;
+
+    public LifecycleProjectEvaluator(ProjectEvaluator delegate) {
+        this.delegate = delegate;
+    }
+
+    public void evaluate(ProjectInternal project, ProjectStateInternal state) {
+        //TODO this is one of the places to look into thread safety when we implement parallel configuration
+        if (state.getExecuted() || state.getExecuting()) {
+            return;
+        }
+
+        ProjectEvaluationListener listener = project.getProjectEvaluationBroadcaster();
+        try {
+            listener.beforeEvaluate(project);
+        } catch (Exception e) {
+            addConfigurationFailure(project, state, e);
+            return;
+        }
+
+        state.setExecuting(true);
+        try {
+            delegate.evaluate(project, state);
+        } catch (Exception e) {
+            addConfigurationFailure(project, state, e);
+        } finally {
+            state.setExecuting(false);
+            state.executed();
+            notifyAfterEvaluate(listener, project, state);
+        }
+    }
+
+    private void notifyAfterEvaluate(ProjectEvaluationListener listener, ProjectInternal project, ProjectStateInternal state) {
+        try {
+            listener.afterEvaluate(project, state);
+        } catch (Exception e) {
+            if (state.hasFailure()) {
+                // Just log this failure, and pass the existing failure out in the project state
+                LOGGER.error("Failed to notify ProjectEvaluationListener.afterEvaluate(), but primary configuration failure takes precedence.", e);
+                return;
+            }
+            addConfigurationFailure(project, state, e);
+        }
+    }
+
+    private void addConfigurationFailure(ProjectInternal project, ProjectStateInternal state, Exception e) {
+        ProjectConfigurationException failure = new ProjectConfigurationException(String.format("A problem occurred configuring %s.", project), e);
+        state.executed(failure);
+    }
+}
\ No newline at end of file
diff --git a/subprojects/core/src/main/groovy/org/gradle/configuration/project/PluginsProjectConfigureActions.java b/subprojects/core/src/main/groovy/org/gradle/configuration/project/PluginsProjectConfigureActions.java
new file mode 100644
index 0000000..834f60b
--- /dev/null
+++ b/subprojects/core/src/main/groovy/org/gradle/configuration/project/PluginsProjectConfigureActions.java
@@ -0,0 +1,34 @@
+/*
+ * Copyright 2012 the original author or authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.gradle.configuration.project;
+
+import org.gradle.api.internal.project.ProjectInternal;
+import org.gradle.internal.service.ServiceLocator;
+
+public class PluginsProjectConfigureActions implements ProjectConfigureAction {
+    private final ServiceLocator serviceLocator;
+
+    public PluginsProjectConfigureActions(ClassLoader pluginsClassLoader) {
+        this.serviceLocator = new ServiceLocator(pluginsClassLoader);
+    }
+
+    public void execute(ProjectInternal project) {
+        for (ProjectConfigureAction configureAction : serviceLocator.getAll(ProjectConfigureAction.class)) {
+            configureAction.execute(project);
+        }
+    }
+}
diff --git a/subprojects/core/src/main/groovy/org/gradle/configuration/project/ProjectConfigurationActionContainer.java b/subprojects/core/src/main/groovy/org/gradle/configuration/project/ProjectConfigurationActionContainer.java
new file mode 100644
index 0000000..1cbf77c
--- /dev/null
+++ b/subprojects/core/src/main/groovy/org/gradle/configuration/project/ProjectConfigurationActionContainer.java
@@ -0,0 +1,40 @@
+/*
+ * Copyright 2013 the original author or authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.gradle.configuration.project;
+
+import groovy.lang.Closure;
+import org.gradle.api.Action;
+import org.gradle.api.internal.project.ProjectInternal;
+
+/**
+ * A container responsible for managing the configuration of a project.
+ */
+public interface ProjectConfigurationActionContainer {
+    void finished();
+
+    Iterable<Action<? super ProjectInternal>> getActions();
+
+    /**
+     * Registers an action to execute to configure the project. Actions are executed in an arbitrary order.
+     */
+    void add(Action<? super ProjectInternal> action);
+
+    /**
+     * Registers an action to execute to configure the project. Actions are executed in an arbitrary order.
+     */
+    void add(Closure action);
+}
diff --git a/subprojects/core/src/main/groovy/org/gradle/configuration/project/ProjectConfigureAction.java b/subprojects/core/src/main/groovy/org/gradle/configuration/project/ProjectConfigureAction.java
new file mode 100644
index 0000000..416d1ce
--- /dev/null
+++ b/subprojects/core/src/main/groovy/org/gradle/configuration/project/ProjectConfigureAction.java
@@ -0,0 +1,30 @@
+/*
+ * Copyright 2012 the original author or authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.gradle.configuration.project;
+
+import org.gradle.api.Action;
+import org.gradle.api.internal.project.ProjectInternal;
+
+/**
+ * Can be implemented by plugins to auto-configure each project.
+ *
+ * <p>Implementations are discovered using the JAR service locator mechanism (see {@link org.gradle.internal.service.ServiceLocator}).
+ * Each action is invoked for each project that is to be configured, before the project has been configured. Actions are executed
+ * in an arbitrary order.
+ */
+public interface ProjectConfigureAction extends Action<ProjectInternal> {
+}
diff --git a/subprojects/core/src/main/groovy/org/gradle/configuration/project/ProjectDependencies2TaskResolver.java b/subprojects/core/src/main/groovy/org/gradle/configuration/project/ProjectDependencies2TaskResolver.java
new file mode 100644
index 0000000..688b698
--- /dev/null
+++ b/subprojects/core/src/main/groovy/org/gradle/configuration/project/ProjectDependencies2TaskResolver.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.configuration.project;
+
+import org.gradle.api.Project;
+import org.gradle.api.Task;
+import org.gradle.api.internal.project.ProjectInternal;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+public class ProjectDependencies2TaskResolver implements ProjectConfigureAction {
+    private static Logger logger = LoggerFactory.getLogger(ProjectDependencies2TaskResolver.class);
+
+    public void execute(ProjectInternal project) {
+        for (Project dependsOnProject : project.getDependsOnProjects()) {
+            logger.debug("Checking task dependencies for project: {} dependsOn: {}", project, dependsOnProject);
+            for (Task task : project.getTasks()) {
+                String taskName = task.getName();
+                Task dependentTask = dependsOnProject.getTasks().findByName(taskName);
+                if (dependentTask != null) {
+                    logger.debug("Setting task dependencies for task: {}", taskName);
+                    task.dependsOn(dependentTask);
+                }
+            }
+        }
+    }
+}
\ No newline at end of file
diff --git a/subprojects/core/src/main/groovy/org/gradle/configuration/project/ProjectEvaluator.java b/subprojects/core/src/main/groovy/org/gradle/configuration/project/ProjectEvaluator.java
new file mode 100644
index 0000000..79c1c4a
--- /dev/null
+++ b/subprojects/core/src/main/groovy/org/gradle/configuration/project/ProjectEvaluator.java
@@ -0,0 +1,23 @@
+/*
+ * Copyright 2010 the original author or authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.gradle.configuration.project;
+
+import org.gradle.api.internal.project.ProjectInternal;
+import org.gradle.api.internal.project.ProjectStateInternal;
+
+public interface ProjectEvaluator {
+    void evaluate(ProjectInternal project, ProjectStateInternal state);
+}
diff --git a/subprojects/core/src/main/groovy/org/gradle/execution/ProjectEvaluatingAction.java b/subprojects/core/src/main/groovy/org/gradle/execution/ProjectEvaluatingAction.java
index d036dec..81b82e8 100644
--- a/subprojects/core/src/main/groovy/org/gradle/execution/ProjectEvaluatingAction.java
+++ b/subprojects/core/src/main/groovy/org/gradle/execution/ProjectEvaluatingAction.java
@@ -23,8 +23,6 @@ import java.util.List;
 
 /**
  * Ensures that projects resolved from the command line task names are evaluated.
- *
- * by Szczepan Faber, created at: 11/22/12
  */
 public class ProjectEvaluatingAction implements BuildConfigurationAction {
 
diff --git a/subprojects/core/src/main/groovy/org/gradle/execution/TaskNameResolver.java b/subprojects/core/src/main/groovy/org/gradle/execution/TaskNameResolver.java
index 424ae41..30d5c37 100644
--- a/subprojects/core/src/main/groovy/org/gradle/execution/TaskNameResolver.java
+++ b/subprojects/core/src/main/groovy/org/gradle/execution/TaskNameResolver.java
@@ -20,54 +20,71 @@ import com.google.common.collect.SetMultimap;
 import org.gradle.api.Project;
 import org.gradle.api.Task;
 import org.gradle.api.internal.project.ProjectInternal;
+import org.gradle.api.tasks.TaskContainer;
 
 import java.util.Collections;
 
 public class TaskNameResolver {
-    
-    public SetMultimap<String, Task> select(String name, Project project) {
+
+    public SetMultimap<String, TaskSelectionResult> select(String name, Project project) {
         return select(name, (ProjectInternal) project, Collections.<Project>emptySet());
     }
 
-    public SetMultimap<String, Task> selectAll(String name, Project project) {
+    public SetMultimap<String, TaskSelectionResult> selectAll(String name, Project project) {
         return select(name, (ProjectInternal) project, project.getSubprojects());
     }
 
-    private SetMultimap<String, Task> select(String name, ProjectInternal project, Iterable<Project> additionalProjects) {
-        SetMultimap<String, Task> selected = LinkedHashMultimap.create();
+    private SetMultimap<String, TaskSelectionResult> select(String name, ProjectInternal project, Iterable<Project> additionalProjects) {
+        SetMultimap<String, TaskSelectionResult> selected = LinkedHashMultimap.create();
         Task task = project.getTasks().findByName(name);
         if (task != null) {
-            selected.put(task.getName(), task);
+            selected.put(task.getName(), new LazyTaskSelectionResult(task.getName(), project.getTasks()));
         } else {
             task = project.getImplicitTasks().findByName(name);
             if (task != null) {
-                selected.put(task.getName(), task);
+                selected.put(task.getName(), new LazyTaskSelectionResult(task.getName(), project.getImplicitTasks()));
             }
         }
         for (Project additionalProject : additionalProjects) {
             task = additionalProject.getTasks().findByName(name);
             if (task != null) {
-                selected.put(task.getName(), task);
+                selected.put(task.getName(), new LazyTaskSelectionResult(task.getName(), additionalProject.getTasks()));
             }
         }
         if (!selected.isEmpty()) {
             return selected;
         }
 
-        for (Task t : project.getTasks()) {
-            selected.put(t.getName(), t);
+        for (String taskName : project.getTasks().getNames()) {
+            selected.put(taskName, new LazyTaskSelectionResult(taskName, project.getTasks()));
         }
-        for (Task t : project.getImplicitTasks()) {
-            if (!selected.containsKey(t.getName())) {
-                selected.put(t.getName(), t);
+        for (String taskName : project.getImplicitTasks().getNames()) {
+            if (!selected.containsKey(taskName)) {
+                selected.put(taskName, new LazyTaskSelectionResult(taskName, project.getImplicitTasks()));
             }
         }
+
         for (Project additionalProject : additionalProjects) {
-            for (Task t : additionalProject.getTasks()) {
-                selected.put(t.getName(), t);
+            for (String taskName : additionalProject.getTasks().getNames()) {
+                selected.put(taskName, new LazyTaskSelectionResult(taskName, additionalProject.getTasks()));
             }
         }
 
         return selected;
     }
+
+
+    public static class LazyTaskSelectionResult implements TaskSelectionResult {
+        private final TaskContainer taskContainer;
+        private final String taskName;
+
+        public LazyTaskSelectionResult(String taskName, TaskContainer tasksContainer) {
+            this.taskContainer = tasksContainer;
+            this.taskName = taskName;
+        }
+
+        public Task getTask() {
+            return taskContainer.getByName(taskName);
+        }
+    }
 }
diff --git a/subprojects/core/src/main/groovy/org/gradle/execution/TaskPathProjectEvaluator.java b/subprojects/core/src/main/groovy/org/gradle/execution/TaskPathProjectEvaluator.java
index 45139f3..d1bc839 100644
--- a/subprojects/core/src/main/groovy/org/gradle/execution/TaskPathProjectEvaluator.java
+++ b/subprojects/core/src/main/groovy/org/gradle/execution/TaskPathProjectEvaluator.java
@@ -21,9 +21,6 @@ import org.gradle.api.internal.project.ProjectInternal;
 import org.gradle.execution.taskpath.ResolvedTaskPath;
 import org.gradle.execution.taskpath.TaskPathResolver;
 
-/**
- * by Szczepan Faber, created at: 1/8/13
- */
 public class TaskPathProjectEvaluator {
 
     private final TaskPathResolver taskPathResolver;
diff --git a/subprojects/core/src/main/groovy/org/gradle/execution/TaskSelectionResult.java b/subprojects/core/src/main/groovy/org/gradle/execution/TaskSelectionResult.java
new file mode 100644
index 0000000..4946a48
--- /dev/null
+++ b/subprojects/core/src/main/groovy/org/gradle/execution/TaskSelectionResult.java
@@ -0,0 +1,23 @@
+/*
+ * Copyright 2013 the original author or authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.gradle.execution;
+
+import org.gradle.api.Task;
+
+public interface TaskSelectionResult {
+    Task getTask();
+}
diff --git a/subprojects/core/src/main/groovy/org/gradle/execution/TaskSelector.java b/subprojects/core/src/main/groovy/org/gradle/execution/TaskSelector.java
index 56a186b..28d8db0 100644
--- a/subprojects/core/src/main/groovy/org/gradle/execution/TaskSelector.java
+++ b/subprojects/core/src/main/groovy/org/gradle/execution/TaskSelector.java
@@ -17,12 +17,16 @@ package org.gradle.execution;
 
 import com.google.common.collect.SetMultimap;
 import org.gradle.api.Task;
+import org.gradle.api.Transformer;
 import org.gradle.api.internal.GradleInternal;
 import org.gradle.api.internal.project.ProjectInternal;
 import org.gradle.execution.taskpath.ResolvedTaskPath;
 import org.gradle.execution.taskpath.TaskPathResolver;
+import org.gradle.util.CollectionUtils;
 import org.gradle.util.NameMatcher;
 
+import java.util.Collection;
+import java.util.HashSet;
 import java.util.Set;
 
 public class TaskSelector {
@@ -40,7 +44,7 @@ public class TaskSelector {
     }
 
     public TaskSelection getSelection(String path) {
-        SetMultimap<String, Task> tasksByName;
+        SetMultimap<String, TaskSelectionResult> tasksByName;
         ProjectInternal project = gradle.getDefaultProject();
         ResolvedTaskPath taskPath = taskPathResolver.resolvePath(path, project);
 
@@ -50,7 +54,7 @@ public class TaskSelector {
             tasksByName = taskNameResolver.selectAll(path, project);
         }
 
-        Set<Task> tasks = tasksByName.get(taskPath.getTaskName());
+        Set<TaskSelectionResult> tasks = tasksByName.get(taskPath.getTaskName());
         if (!tasks.isEmpty()) {
             // An exact match
             return new TaskSelection(path, tasks);
@@ -58,9 +62,7 @@ public class TaskSelector {
 
         NameMatcher matcher = new NameMatcher();
         String actualName = matcher.find(taskPath.getTaskName(), tasksByName.keySet());
-
         if (actualName != null) {
-            // A partial match
             return new TaskSelection(taskPath.getPrefix() + actualName, tasksByName.get(actualName));
         }
 
@@ -69,11 +71,10 @@ public class TaskSelector {
 
     public static class TaskSelection {
         private String taskName;
-        private Set<Task> tasks;
-
-        public TaskSelection(String taskName, Set<Task> tasks) {
+        private Collection<TaskSelectionResult> taskSelectionResult;
+        public TaskSelection(String taskName, Set<TaskSelectionResult> tasks) {
             this.taskName = taskName;
-            this.tasks = tasks;
+            taskSelectionResult = tasks;
         }
 
         public String getTaskName() {
@@ -81,7 +82,11 @@ public class TaskSelector {
         }
 
         public Set<Task> getTasks() {
-            return tasks;
+            return new HashSet<Task>(CollectionUtils.collect(taskSelectionResult, new Transformer<Task, TaskSelectionResult>() {
+                public Task transform(TaskSelectionResult original) {
+                    return original.getTask();
+                }
+            }));
         }
     }
 }
diff --git a/subprojects/core/src/main/groovy/org/gradle/execution/commandline/CommandLineTaskConfigurer.java b/subprojects/core/src/main/groovy/org/gradle/execution/commandline/CommandLineTaskConfigurer.java
index e7139cb..a0fa0fd 100644
--- a/subprojects/core/src/main/groovy/org/gradle/execution/commandline/CommandLineTaskConfigurer.java
+++ b/subprojects/core/src/main/groovy/org/gradle/execution/commandline/CommandLineTaskConfigurer.java
@@ -23,6 +23,7 @@ import org.gradle.cli.CommandLineArgumentException;
 import org.gradle.cli.CommandLineParser;
 import org.gradle.cli.ParsedCommandLine;
 import org.gradle.cli.ParsedCommandLineOption;
+import org.gradle.internal.reflect.JavaReflectionUtil;
 import org.gradle.util.JavaMethod;
 
 import java.lang.reflect.Method;
@@ -31,9 +32,6 @@ import java.util.HashMap;
 import java.util.List;
 import java.util.Map;
 
-/**
- * by Szczepan Faber, created at: 9/5/12
- */
 public class CommandLineTaskConfigurer {
 
     public List<String> configureTasks(Collection<Task> tasks, List<String> arguments) {
@@ -59,7 +57,7 @@ public class CommandLineTaskConfigurer {
                         if (method.getParameterTypes().length > 0 && !hasSingleBooleanParameter(method)) {
                             option.hasArgument();
                         }
-                        options.put(optionName, JavaMethod.create(Object.class, Object.class, method));
+                        options.put(optionName, JavaReflectionUtil.method(Object.class, Object.class, method));
                     }
                 }
             }
diff --git a/subprojects/core/src/main/groovy/org/gradle/execution/commandline/CommandLineTaskParser.java b/subprojects/core/src/main/groovy/org/gradle/execution/commandline/CommandLineTaskParser.java
index 291b5d7..dd181ca 100644
--- a/subprojects/core/src/main/groovy/org/gradle/execution/commandline/CommandLineTaskParser.java
+++ b/subprojects/core/src/main/groovy/org/gradle/execution/commandline/CommandLineTaskParser.java
@@ -26,9 +26,6 @@ import java.util.LinkedList;
 import java.util.List;
 import java.util.Set;
 
-/**
- * by Szczepan Faber, created at: 10/8/12
- */
 public class CommandLineTaskParser {
 
     CommandLineTaskConfigurer taskConfigurer =  new CommandLineTaskConfigurer();
diff --git a/subprojects/core/src/main/groovy/org/gradle/execution/taskgraph/AbstractTaskPlanExecutor.java b/subprojects/core/src/main/groovy/org/gradle/execution/taskgraph/AbstractTaskPlanExecutor.java
new file mode 100644
index 0000000..fbc86c3
--- /dev/null
+++ b/subprojects/core/src/main/groovy/org/gradle/execution/taskgraph/AbstractTaskPlanExecutor.java
@@ -0,0 +1,87 @@
+/*
+ * Copyright 2013 the original author or authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.gradle.execution.taskgraph;
+
+import org.gradle.api.execution.TaskExecutionListener;
+import org.gradle.api.internal.TaskInternal;
+import org.gradle.api.logging.Logger;
+import org.gradle.api.logging.Logging;
+
+import static org.gradle.util.Clock.prettyTime;
+
+abstract class AbstractTaskPlanExecutor implements TaskPlanExecutor {
+    private static final Logger LOGGER = Logging.getLogger(AbstractTaskPlanExecutor.class);
+    private final Object lock = new Object();
+
+    protected Runnable taskWorker(TaskExecutionPlan taskExecutionPlan, TaskExecutionListener taskListener) {
+        return new TaskExecutorWorker(taskExecutionPlan, taskListener);
+    }
+
+    private class TaskExecutorWorker implements Runnable {
+        private final TaskExecutionPlan taskExecutionPlan;
+        private final TaskExecutionListener taskListener;
+
+        private TaskExecutorWorker(TaskExecutionPlan taskExecutionPlan, TaskExecutionListener taskListener) {
+            this.taskExecutionPlan = taskExecutionPlan;
+            this.taskListener = taskListener;
+        }
+
+        public void run() {
+            long busy = 0;
+            long start = System.currentTimeMillis();
+            TaskInfo task;
+            while ((task = taskExecutionPlan.getTaskToExecute()) != null) {
+                final String taskPath = task.getTask().getPath();
+                LOGGER.info("{} ({}) started.", taskPath, Thread.currentThread());
+                long startTask = System.currentTimeMillis();
+                processTask(task);
+                long taskDuration = System.currentTimeMillis() - startTask;
+                busy += taskDuration;
+                LOGGER.info("{} ({}) completed. Took {}.", taskPath, Thread.currentThread(), prettyTime(taskDuration));
+            }
+            long total = System.currentTimeMillis() - start;
+            //TODO SF it would be nice to print one-line statement that concludes the utilisation of the worker threads
+            LOGGER.debug("Task worker [{}] finished, busy: {}, idle: {}", Thread.currentThread(), prettyTime(busy), prettyTime(total - busy));
+        }
+
+        protected void processTask(TaskInfo taskInfo) {
+            try {
+                executeTask(taskInfo);
+            } catch (Throwable e) {
+                taskInfo.setExecutionFailure(e);
+            } finally {
+                taskExecutionPlan.taskComplete(taskInfo);
+            }
+        }
+
+        // TODO:PARALLEL It would be good to move this logic into a TaskExecuter wrapper, but we'd need a way to give it a TaskExecutionListener that
+        // is wired to the various add/remove listener methods on TaskExecutionGraph
+        private void executeTask(TaskInfo taskInfo) {
+            TaskInternal task = taskInfo.getTask();
+            synchronized (lock) {
+                taskListener.beforeExecute(task);
+            }
+            try {
+                task.executeWithoutThrowingTaskFailure();
+            } finally {
+                synchronized (lock) {
+                    taskListener.afterExecute(task, task.getState());
+                }
+            }
+        }
+    }
+}
diff --git a/subprojects/core/src/main/groovy/org/gradle/execution/taskgraph/DefaultTaskExecutionPlan.java b/subprojects/core/src/main/groovy/org/gradle/execution/taskgraph/DefaultTaskExecutionPlan.java
index 912c5da..cfbc7d4 100644
--- a/subprojects/core/src/main/groovy/org/gradle/execution/taskgraph/DefaultTaskExecutionPlan.java
+++ b/subprojects/core/src/main/groovy/org/gradle/execution/taskgraph/DefaultTaskExecutionPlan.java
@@ -18,6 +18,7 @@ package org.gradle.execution.taskgraph;
 
 import org.gradle.api.CircularReferenceException;
 import org.gradle.api.Task;
+import org.gradle.api.Transformer;
 import org.gradle.api.internal.TaskInternal;
 import org.gradle.api.internal.tasks.CachingTaskDependencyResolveContext;
 import org.gradle.api.specs.Spec;
@@ -25,7 +26,14 @@ import org.gradle.api.specs.Specs;
 import org.gradle.execution.MultipleBuildFailures;
 import org.gradle.execution.TaskFailureHandler;
 import org.gradle.internal.UncheckedException;
-
+import org.gradle.internal.graph.CachingDirectedGraphWalker;
+import org.gradle.internal.graph.DirectedGraph;
+import org.gradle.internal.graph.DirectedGraphRenderer;
+import org.gradle.internal.graph.GraphNodeRenderer;
+import org.gradle.logging.StyledTextOutput;
+import org.gradle.util.CollectionUtils;
+
+import java.io.StringWriter;
 import java.util.*;
 import java.util.concurrent.locks.Condition;
 import java.util.concurrent.locks.Lock;
@@ -38,6 +46,9 @@ import java.util.concurrent.locks.ReentrantLock;
 class DefaultTaskExecutionPlan implements TaskExecutionPlan {
     private final Lock lock = new ReentrantLock();
     private final Condition condition = lock.newCondition();
+    private final Set<TaskInfo> tasksInUnknownState = new LinkedHashSet<TaskInfo>();
+    private final Set<TaskInfo> entryTasks = new LinkedHashSet<TaskInfo>();
+    private final TaskDependencyGraph graph = new TaskDependencyGraph();
     private final LinkedHashMap<Task, TaskInfo> executionPlan = new LinkedHashMap<Task, TaskInfo>();
     private final List<Throwable> failures = new ArrayList<Throwable>();
     private Spec<? super Task> filter = Specs.satisfyAll();
@@ -46,57 +57,227 @@ class DefaultTaskExecutionPlan implements TaskExecutionPlan {
     private final List<String> runningProjects = new ArrayList<String>();
 
     public void addToTaskGraph(Collection<? extends Task> tasks) {
-        List<Task> queue = new ArrayList<Task>(tasks);
-        Collections.sort(queue);
+        List<TaskInfo> queue = new ArrayList<TaskInfo>();
+
+        List<Task> sortedTasks = new ArrayList<Task>(tasks);
+        Collections.sort(sortedTasks);
+        for (Task task : sortedTasks) {
+            TaskInfo node = graph.addNode(task);
+            if (node.getMustNotRun()) {
+                requireWithDependencies(node);
+            } else {
+                node.require();
+            }
+            entryTasks.add(node);
+            queue.add(node);
+        }
 
-        Set<Task> visiting = new HashSet<Task>();
+        Set<TaskInfo> visiting = new HashSet<TaskInfo>();
         CachingTaskDependencyResolveContext context = new CachingTaskDependencyResolveContext();
 
         while (!queue.isEmpty()) {
-            Task task = queue.get(0);
-            if (!filter.isSatisfiedBy(task)) {
-                // Filtered - skip
+            TaskInfo node = queue.get(0);
+            if (node.getDependenciesProcessed()) {
+                // Have already visited this task - skip it
                 queue.remove(0);
                 continue;
             }
-            if (executionPlan.containsKey(task)) {
-                // Already in plan - skip
+
+            TaskInternal task = node.getTask();
+            boolean filtered = !filter.isSatisfiedBy(task);
+            if (filtered) {
+                // Task is not required - skip it
                 queue.remove(0);
+                node.dependenciesProcessed();
+                node.doNotRequire();
                 continue;
             }
 
-            if (visiting.add(task)) {
+            if (visiting.add(node)) {
                 // Have not seen this task before - add its dependencies to the head of the queue and leave this
                 // task in the queue
-                Set<Task> dependsOnTasks = new TreeSet<Task>(Collections.reverseOrder());
-                dependsOnTasks.addAll(context.getDependencies(task));
+                Set<? extends Task> dependsOnTasks = context.getDependencies(task);
                 for (Task dependsOnTask : dependsOnTasks) {
-                    if (visiting.contains(dependsOnTask)) {
-                        throw new CircularReferenceException(String.format(
-                                "Circular dependency between tasks. Cycle includes [%s, %s].", task, dependsOnTask));
+                    TaskInfo targetNode = graph.addNode(dependsOnTask);
+                    node.addHardSuccessor(targetNode);
+                    if (!visiting.contains(targetNode)) {
+                        queue.add(0, targetNode);
+                    }
+                }
+                for (Task finalizerTask : task.getFinalizedBy().getDependencies(task)) {
+                    TaskInfo targetNode = graph.addNode(finalizerTask);
+                    addFinalizerNode(node, targetNode);
+                    if (!visiting.contains(targetNode)) {
+                        queue.add(0, targetNode);
+                    }
+                }
+                for (Task mustRunAfter : task.getMustRunAfter().getDependencies(task)) {
+                    TaskInfo targetNode = graph.addNode(mustRunAfter);
+                    node.addSoftSuccessor(targetNode);
+                }
+                if (node.isRequired()) {
+                    for (TaskInfo hardSuccessor : node.getHardSuccessors()) {
+                        hardSuccessor.require();
+                    }
+                } else {
+                    tasksInUnknownState.add(node);
+                }
+            } else {
+                // Have visited this task's dependencies - add it to the graph
+                queue.remove(0);
+                visiting.remove(node);
+                node.dependenciesProcessed();
+            }
+        }
+        resolveTasksInUnknownState();
+    }
+
+    private void resolveTasksInUnknownState() {
+        List<TaskInfo> queue = new ArrayList<TaskInfo>(tasksInUnknownState);
+        Set<TaskInfo> visiting = new HashSet<TaskInfo>();
+
+        while (!queue.isEmpty()) {
+            TaskInfo task = queue.get(0);
+            if (task.isInKnownState()) {
+                queue.remove(0);
+                continue;
+            }
+
+            if (visiting.add(task)) {
+                for (TaskInfo hardPredecessor : task.getHardPredecessors()) {
+                    if (!visiting.contains(hardPredecessor)) {
+                        queue.add(0, hardPredecessor);
                     }
-                    queue.add(0, dependsOnTask);
                 }
             } else {
-                // Have visited this task's dependencies - add it to the end of the plan
                 queue.remove(0);
                 visiting.remove(task);
-                Set<TaskInfo> dependencies = new HashSet<TaskInfo>();
-                for (Task dependency : context.getDependencies(task)) {
-                    TaskInfo dependencyInfo = executionPlan.get(dependency);
-                    if (dependencyInfo != null) {
-                        dependencies.add(dependencyInfo);
+                task.mustNotRun();
+                for (TaskInfo predecessor : task.getHardPredecessors()) {
+                    assert predecessor.isRequired() || predecessor.getMustNotRun();
+                    if (predecessor.isRequired()) {
+                        task.require();
+                        break;
+                    }
+                }
+            }
+        }
+    }
+
+    private void addFinalizerNode(TaskInfo node, TaskInfo finalizerNode) {
+        if (filter.isSatisfiedBy(finalizerNode.getTask())) {
+            node.addFinalizer(finalizerNode);
+            if (!finalizerNode.isInKnownState()) {
+                finalizerNode.mustNotRun();
+            }
+            finalizerNode.addSoftSuccessor(node);
+        }
+    }
+
+    private <T> void addAllReversed(List<T> list, TreeSet<T> set) {
+        List<T> elements = CollectionUtils.toList(set);
+        Collections.reverse(elements);
+        list.addAll(elements);
+    }
+
+    private void requireWithDependencies(TaskInfo taskInfo) {
+        if (taskInfo.getMustNotRun() && filter.isSatisfiedBy(taskInfo.getTask())) {
+            taskInfo.require();
+            for (TaskInfo dependency : taskInfo.getHardSuccessors()) {
+                requireWithDependencies(dependency);
+            }
+        }
+    }
+
+    public void determineExecutionPlan() {
+        List<TaskInfo> nodeQueue = new ArrayList<TaskInfo>(entryTasks);
+        Set<TaskInfo> visitingNodes = new HashSet<TaskInfo>();
+        while (!nodeQueue.isEmpty()) {
+            TaskInfo taskNode = nodeQueue.get(0);
+
+            if (taskNode.isIncludeInGraph() || executionPlan.containsKey(taskNode.getTask())) {
+                nodeQueue.remove(0);
+                continue;
+            }
+
+            if (visitingNodes.add(taskNode)) {
+                // Have not seen this task before - add its dependencies to the head of the queue and leave this
+                // task in the queue
+                ArrayList<TaskInfo> dependsOnTasks = new ArrayList<TaskInfo>();
+                addAllReversed(dependsOnTasks, taskNode.getHardSuccessors());
+                addAllReversed(dependsOnTasks, taskNode.getSoftSuccessors());
+                for (TaskInfo dependsOnTask : dependsOnTasks) {
+                    if (visitingNodes.contains(dependsOnTask)) {
+                        onOrderingCycle();
+                    }
+                    nodeQueue.add(0, dependsOnTask);
+                }
+            } else {
+                // Have visited this task's dependencies - add it to the end of the plan
+                nodeQueue.remove(0);
+                visitingNodes.remove(taskNode);
+                executionPlan.put(taskNode.getTask(), taskNode);
+                ArrayList<TaskInfo> finalizerTasks = new ArrayList<TaskInfo>();
+                addAllReversed(finalizerTasks, taskNode.getFinalizers());
+                for (TaskInfo finalizer : finalizerTasks) {
+                    if (!visitingNodes.contains(finalizer)) {
+                        nodeQueue.add(finalizerTaskPosition(finalizer, nodeQueue), finalizer);
                     }
-                    // else - the dependency has been filtered, so ignore it
                 }
-                executionPlan.put(task, new TaskInfo((TaskInternal) task, dependencies));
             }
         }
     }
 
+    private int finalizerTaskPosition(TaskInfo finalizer, final List<TaskInfo> nodeQueue) {
+        if (nodeQueue.size() == 0) {
+            return 0;
+        }
+
+        ArrayList<TaskInfo> dependsOnTasks = new ArrayList<TaskInfo>();
+        dependsOnTasks.addAll(finalizer.getHardSuccessors());
+        dependsOnTasks.addAll(finalizer.getSoftSuccessors());
+        List<Integer> dependsOnTaskIndexes = CollectionUtils.collect(dependsOnTasks, new Transformer<Integer, TaskInfo>() {
+            public Integer transform(TaskInfo dependsOnTask) {
+                return nodeQueue.indexOf(dependsOnTask);
+            }
+        });
+        return Collections.max(dependsOnTaskIndexes) + 1;
+    }
+
+    private void onOrderingCycle() {
+        CachingDirectedGraphWalker<TaskInfo, Void> graphWalker = new CachingDirectedGraphWalker<TaskInfo, Void>(new DirectedGraph<TaskInfo, Void>() {
+            public void getNodeValues(TaskInfo node, Collection<? super Void> values, Collection<? super TaskInfo> connectedNodes) {
+                connectedNodes.addAll(node.getHardSuccessors());
+                connectedNodes.addAll(node.getSoftSuccessors());
+            }
+        });
+        graphWalker.add(entryTasks);
+        final List<TaskInfo> firstCycle = new ArrayList<TaskInfo>(graphWalker.findCycles().get(0));
+        Collections.sort(firstCycle);
+
+        DirectedGraphRenderer<TaskInfo> graphRenderer = new DirectedGraphRenderer<TaskInfo>(new GraphNodeRenderer<TaskInfo>() {
+            public void renderTo(TaskInfo node, StyledTextOutput output) {
+                output.withStyle(StyledTextOutput.Style.Identifier).text(node.getTask().getPath());
+            }
+        }, new DirectedGraph<TaskInfo, Object>() {
+            public void getNodeValues(TaskInfo node, Collection<? super Object> values, Collection<? super TaskInfo> connectedNodes) {
+                for (TaskInfo dependency : firstCycle) {
+                    if (node.getHardSuccessors().contains(dependency) || node.getSoftSuccessors().contains(dependency)) {
+                        connectedNodes.add(dependency);
+                    }
+                }
+            }
+        });
+        StringWriter writer = new StringWriter();
+        graphRenderer.renderTo(firstCycle.get(0), writer);
+        throw new CircularReferenceException(String.format("Circular dependency between the following tasks:%n%s", writer.toString()));
+    }
+
     public void clear() {
         lock.lock();
         try {
+            graph.clear();
+            entryTasks.clear();
             executionPlan.clear();
             failures.clear();
             runningProjects.clear();
@@ -117,45 +298,10 @@ class DefaultTaskExecutionPlan implements TaskExecutionPlan {
         this.failureHandler = handler;
     }
 
-    public TaskInfo getTaskToExecute(Spec<TaskInfo> criteria) {
-        lock.lock();
-        try {
-
-            TaskInfo nextMatching;
-            while ((nextMatching = getNextReadyAndMatching(criteria)) != null) {
-                while (!nextMatching.allDependenciesComplete()) {
-                    try {
-                        condition.await();
-                    } catch (InterruptedException e) {
-                        throw new RuntimeException(e);
-                    }
-                }
-
-                // The task state could have been modified while we waited for dependency completion. Check that it is still 'ready'.
-                if (!nextMatching.isReady()) {
-                    continue;
-                }
-
-                if (nextMatching.allDependenciesSuccessful()) {
-                    nextMatching.startExecution();
-                    return nextMatching;
-                } else {
-                    nextMatching.skipExecution();
-                    condition.signalAll();
-                }
-            }
-
-            return null;
-
-        } finally {
-            lock.unlock();
-        }
-    }
-
     public TaskInfo getTaskToExecute() {
         lock.lock();
         try {
-            while(true) {
+            while (true) {
                 TaskInfo nextMatching = null;
                 boolean allTasksComplete = true;
                 for (TaskInfo taskInfo : executionPlan.values()) {
@@ -190,18 +336,10 @@ class DefaultTaskExecutionPlan implements TaskExecutionPlan {
         }
     }
 
-    private TaskInfo getNextReadyAndMatching(Spec<TaskInfo> criteria) {
-        for (TaskInfo taskInfo : executionPlan.values()) {
-            if (taskInfo.isReady() && criteria.isSatisfiedBy(taskInfo)) {
-                return taskInfo;
-            }
-        }
-        return null;
-    }
-
     public void taskComplete(TaskInfo taskInfo) {
         lock.lock();
         try {
+            enforceFinalizerTasks(taskInfo);
             if (taskInfo.isFailed()) {
                 handleFailure(taskInfo);
             }
@@ -214,6 +352,23 @@ class DefaultTaskExecutionPlan implements TaskExecutionPlan {
         }
     }
 
+    private void enforceFinalizerTasks(TaskInfo taskInfo) {
+        for (TaskInfo finalizerNode : taskInfo.getFinalizers()) {
+            if (finalizerNode.isRequired() || finalizerNode.getMustNotRun()) {
+                enforceWithDependencies(finalizerNode);
+            }
+        }
+    }
+
+    private void enforceWithDependencies(TaskInfo node) {
+        for (TaskInfo dependencyNode : node.getHardSuccessors()) {
+            enforceWithDependencies(dependencyNode);
+        }
+        if (node.getMustNotRun() || node.isRequired()) {
+            node.enforceRun();
+        }
+    }
+
     private void handleFailure(TaskInfo taskInfo) {
         Throwable executionFailure = taskInfo.getExecutionFailure();
         if (executionFailure != null) {
@@ -235,9 +390,9 @@ class DefaultTaskExecutionPlan implements TaskExecutionPlan {
     }
 
     private void abortExecution() {
-        // Allow currently executing tasks to complete, but skip everything else.
+        // Allow currently executing and enforced tasks to complete, but skip everything else.
         for (TaskInfo taskInfo : executionPlan.values()) {
-            if (taskInfo.isReady()) {
+            if (taskInfo.isRequired()) {
                 taskInfo.skipExecution();
             }
         }
diff --git a/subprojects/core/src/main/groovy/org/gradle/execution/taskgraph/DefaultTaskGraphExecuter.java b/subprojects/core/src/main/groovy/org/gradle/execution/taskgraph/DefaultTaskGraphExecuter.java
index dddbae7..5b319e8 100644
--- a/subprojects/core/src/main/groovy/org/gradle/execution/taskgraph/DefaultTaskGraphExecuter.java
+++ b/subprojects/core/src/main/groovy/org/gradle/execution/taskgraph/DefaultTaskGraphExecuter.java
@@ -34,17 +34,18 @@ import java.util.LinkedHashSet;
 import java.util.List;
 import java.util.Set;
 
-/**
- * @author Hans Dockter
- */
 public class DefaultTaskGraphExecuter implements TaskGraphExecuter {
     private static Logger logger = LoggerFactory.getLogger(DefaultTaskGraphExecuter.class);
 
+    private enum TaskGraphState {
+        EMPTY, DIRTY, POPULATED
+    }
+
     private final TaskPlanExecutor taskPlanExecutor;
     private final ListenerBroadcast<TaskExecutionGraphListener> graphListeners;
     private final ListenerBroadcast<TaskExecutionListener> taskListeners;
     private final DefaultTaskExecutionPlan taskExecutionPlan = new DefaultTaskExecutionPlan();
-    private boolean populated;
+    private TaskGraphState taskGraphState = TaskGraphState.EMPTY;
 
     public DefaultTaskGraphExecuter(ListenerManager listenerManager, TaskPlanExecutor taskPlanExecutor) {
         this.taskPlanExecutor = taskPlanExecutor;
@@ -52,14 +53,15 @@ public class DefaultTaskGraphExecuter implements TaskGraphExecuter {
         taskListeners = listenerManager.createAnonymousBroadcaster(TaskExecutionListener.class);
     }
 
-    public void useFilter(Spec<? super Task> filter) {
-        taskExecutionPlan.useFilter(filter);
-    }
-
     public void useFailureHandler(TaskFailureHandler handler) {
         taskExecutionPlan.useFailureHandler(handler);
     }
 
+    public void useFilter(Spec<? super Task> filter) {
+        taskExecutionPlan.useFilter(filter);
+        taskGraphState = TaskGraphState.DIRTY;
+    }
+
     public void addTasks(Iterable<? extends Task> tasks) {
         assert tasks != null;
 
@@ -70,14 +72,14 @@ public class DefaultTaskGraphExecuter implements TaskGraphExecuter {
             taskSet.add(task);
         }
         taskExecutionPlan.addToTaskGraph(taskSet);
-        populated = true;
+        taskGraphState = TaskGraphState.DIRTY;
 
         logger.debug("Timing: Creating the DAG took " + clock.getTime());
     }
 
     public void execute() {
-        assertPopulated();
         Clock clock = new Clock();
+        ensurePopulated();
 
         graphListeners.getSource().graphPopulated(this);
         try {
@@ -117,12 +119,12 @@ public class DefaultTaskGraphExecuter implements TaskGraphExecuter {
     }
 
     public boolean hasTask(Task task) {
-        assertPopulated();
+        ensurePopulated();
         return taskExecutionPlan.getTasks().contains(task);
     }
 
     public boolean hasTask(String path) {
-        assertPopulated();
+        ensurePopulated();
         assert path != null && path.length() > 0;
         for (Task task : taskExecutionPlan.getTasks()) {
             if (task.getPath().equals(path)) {
@@ -133,14 +135,20 @@ public class DefaultTaskGraphExecuter implements TaskGraphExecuter {
     }
 
     public List<Task> getAllTasks() {
-        assertPopulated();
+        ensurePopulated();
         return taskExecutionPlan.getTasks();
     }
 
-    private void assertPopulated() {
-        if (!populated) {
-            throw new IllegalStateException(
-                    "Task information is not available, as this task execution graph has not been populated.");
+    private void ensurePopulated() {
+        switch (taskGraphState) {
+            case EMPTY:
+                throw new IllegalStateException(
+                        "Task information is not available, as this task execution graph has not been populated.");
+            case DIRTY:
+                taskExecutionPlan.determineExecutionPlan();
+                taskGraphState = TaskGraphState.POPULATED;
+                return;
+            case POPULATED:
         }
     }
 }
diff --git a/subprojects/core/src/main/groovy/org/gradle/execution/taskgraph/DefaultTaskPlanExecutor.java b/subprojects/core/src/main/groovy/org/gradle/execution/taskgraph/DefaultTaskPlanExecutor.java
index b353a6d..ac30fd6 100644
--- a/subprojects/core/src/main/groovy/org/gradle/execution/taskgraph/DefaultTaskPlanExecutor.java
+++ b/subprojects/core/src/main/groovy/org/gradle/execution/taskgraph/DefaultTaskPlanExecutor.java
@@ -17,41 +17,22 @@
 package org.gradle.execution.taskgraph;
 
 import org.gradle.api.execution.TaskExecutionListener;
-import org.gradle.api.internal.TaskInternal;
-import org.gradle.api.specs.Spec;
-import org.gradle.api.specs.Specs;
+import org.gradle.api.internal.changedetection.state.TaskArtifactStateCacheAccess;
 
-class DefaultTaskPlanExecutor implements TaskPlanExecutor {
+class DefaultTaskPlanExecutor extends AbstractTaskPlanExecutor {
+    private final TaskArtifactStateCacheAccess stateCacheAccess;
 
-    public void process(TaskExecutionPlan taskExecutionPlan, TaskExecutionListener taskListener) {
-        Spec<TaskInfo> anyTask = Specs.satisfyAll();
-        TaskInfo taskInfo = taskExecutionPlan.getTaskToExecute(anyTask);
-        while (taskInfo != null) {
-            processTask(taskInfo, taskExecutionPlan, taskListener);
-            taskInfo = taskExecutionPlan.getTaskToExecute(anyTask);
-        }
-        taskExecutionPlan.awaitCompletion();
+    DefaultTaskPlanExecutor(TaskArtifactStateCacheAccess stateCacheAccess) {
+        this.stateCacheAccess = stateCacheAccess;
     }
 
-    protected void processTask(TaskInfo taskInfo, TaskExecutionPlan taskExecutionPlan, TaskExecutionListener taskListener) {
-        try {
-            executeTask(taskInfo, taskListener);
-        } catch (Throwable e) {
-            taskInfo.setExecutionFailure(e);
-        } finally {
-            taskExecutionPlan.taskComplete(taskInfo);
-        }
-    }
-
-    // TODO:PARALLEL It would be good to move this logic into a TaskExecuter wrapper, but we'd need a way to give it a TaskExecutionListener that
-    // is wired to the various add/remove listener methods on TaskExecutionGraph
-    private void executeTask(TaskInfo taskInfo, TaskExecutionListener taskListener) {
-        TaskInternal task = taskInfo.getTask();
-        taskListener.beforeExecute(task);
-        try {
-            task.executeWithoutThrowingTaskFailure();
-        } finally {
-            taskListener.afterExecute(task, task.getState());
-        }
+    public void process(final TaskExecutionPlan taskExecutionPlan, final TaskExecutionListener taskListener) {
+        //At the very beginning, we unlock the cache access. The synchronisation via 'useCache' is pushed down and ine grained.
+        stateCacheAccess.longRunningOperation("Execute tasks", new Runnable() {
+            public void run() {
+                taskWorker(taskExecutionPlan, taskListener).run();
+                taskExecutionPlan.awaitCompletion();
+            }
+        });
     }
 }
diff --git a/subprojects/core/src/main/groovy/org/gradle/execution/taskgraph/ParallelTaskPlanExecutor.java b/subprojects/core/src/main/groovy/org/gradle/execution/taskgraph/ParallelTaskPlanExecutor.java
index 29af40c..7abedcc 100644
--- a/subprojects/core/src/main/groovy/org/gradle/execution/taskgraph/ParallelTaskPlanExecutor.java
+++ b/subprojects/core/src/main/groovy/org/gradle/execution/taskgraph/ParallelTaskPlanExecutor.java
@@ -19,58 +19,59 @@ package org.gradle.execution.taskgraph;
 import org.gradle.api.Project;
 import org.gradle.api.Task;
 import org.gradle.api.execution.TaskExecutionListener;
-import org.gradle.api.internal.changedetection.TaskArtifactStateCacheAccess;
+import org.gradle.api.internal.changedetection.state.TaskArtifactStateCacheAccess;
 import org.gradle.api.logging.Logger;
 import org.gradle.api.logging.Logging;
+import org.gradle.internal.concurrent.DefaultExecutorFactory;
+import org.gradle.internal.concurrent.ExecutorFactory;
+import org.gradle.internal.concurrent.StoppableExecutor;
 
 import java.util.ArrayList;
 import java.util.LinkedHashSet;
 import java.util.List;
 import java.util.Set;
 
-import static org.gradle.util.Clock.prettyTime;
-
-class ParallelTaskPlanExecutor extends DefaultTaskPlanExecutor {
+class ParallelTaskPlanExecutor extends AbstractTaskPlanExecutor {
     private static final Logger LOGGER = Logging.getLogger(ParallelTaskPlanExecutor.class);
-
-    private final List<Thread> executorThreads = new ArrayList<Thread>();
-    private final TaskArtifactStateCacheAccess stateCacheAccess;
     private final int executorCount;
+    private final TaskArtifactStateCacheAccess cacheAccess;
 
     public ParallelTaskPlanExecutor(TaskArtifactStateCacheAccess cacheAccess, int numberOfParallelExecutors) {
+        this.cacheAccess = cacheAccess;
         if (numberOfParallelExecutors < 1) {
             throw new IllegalArgumentException("Not a valid number of parallel executors: " + numberOfParallelExecutors);
         }
 
-        LOGGER.info("Using {} parallel executor threads", numberOfParallelExecutors);
-
-        this.stateCacheAccess = cacheAccess;
         this.executorCount = numberOfParallelExecutors;
     }
 
     public void process(final TaskExecutionPlan taskExecutionPlan, final TaskExecutionListener taskListener) {
-        stateCacheAccess.longRunningOperation("Executing all tasks", new Runnable() {
+        // The main thread holds the lock for the task cache. Need to release the lock while executing the tasks.
+        // This locking needs to be pushed down closer to the things that need the lock and removed from here.
+        cacheAccess.longRunningOperation("Executing all tasks", new Runnable() {
             public void run() {
-                doProcess(taskExecutionPlan, taskListener);
-                // TODO This needs to wait until all tasks have been executed, not just started....
-                taskExecutionPlan.awaitCompletion();
+                DefaultExecutorFactory factory = new DefaultExecutorFactory();
+                try {
+                    doProcess(taskExecutionPlan, taskListener, factory);
+                    taskExecutionPlan.awaitCompletion();
+                } finally {
+                    factory.stop();
+                }
             }
         });
     }
 
-    private void doProcess(TaskExecutionPlan taskExecutionPlan, TaskExecutionListener taskListener) {
+    private void doProcess(TaskExecutionPlan taskExecutionPlan, TaskExecutionListener taskListener, ExecutorFactory factory) {
         List<Project> projects = getAllProjects(taskExecutionPlan);
         int numExecutors = Math.min(executorCount, projects.size());
-        numExecutors = Math.min(numExecutors, 4);
 
-        for (int i = 0; i < numExecutors; i++) {
-            TaskExecutorWorker worker = new TaskExecutorWorker(taskExecutionPlan, taskListener);
-            executorThreads.add(new Thread(worker));
-        }
+        LOGGER.info("Using {} parallel executor threads", numExecutors);
 
-        for (Thread executorThread : executorThreads) {
+        for (int i = 0; i < numExecutors; i++) {
+            Runnable worker = taskWorker(taskExecutionPlan, taskListener);
+            StoppableExecutor executor = factory.create("Task worker " + (i+1));
             // TODO A bunch more stuff to contextualise the thread
-            executorThread.start();
+            executor.execute(worker);
         }
     }
 
@@ -81,41 +82,4 @@ class ParallelTaskPlanExecutor extends DefaultTaskPlanExecutor {
         }
         return new ArrayList<Project>(uniqueProjects);
     }
-
-    private class TaskExecutorWorker implements Runnable {
-        private final TaskExecutionPlan taskExecutionPlan;
-        private final TaskExecutionListener taskListener;
-        private long busyMs;
-        private long waitedForCacheMs;
-
-        private TaskExecutorWorker(TaskExecutionPlan taskExecutionPlan, TaskExecutionListener taskListener) {
-            this.taskExecutionPlan = taskExecutionPlan;
-            this.taskListener = taskListener;
-        }
-
-        public void run() {
-            long start = System.currentTimeMillis();
-            TaskInfo task;
-            while((task = taskExecutionPlan.getTaskToExecute()) != null) {
-                executeTaskWithCacheLock(task);
-            }
-            long total = System.currentTimeMillis() - start;
-            LOGGER.info("Parallel worker [{}] stopped, busy: {}, idle: {}, waited for cache: {}", Thread.currentThread(), prettyTime(busyMs), prettyTime(total - busyMs), prettyTime(waitedForCacheMs));
-        }
-
-        private void executeTaskWithCacheLock(final TaskInfo taskInfo) {
-            final String taskPath = taskInfo.getTask().getPath();
-            LOGGER.info(taskPath + " (" + Thread.currentThread() + " - start");
-            final long start = System.currentTimeMillis();
-            stateCacheAccess.useCache("Executing " + taskPath, new Runnable() {
-                public void run() {
-                    waitedForCacheMs += System.currentTimeMillis() - start;
-                    processTask(taskInfo, taskExecutionPlan, taskListener);
-                }
-            });
-            busyMs += System.currentTimeMillis() - start;
-
-            LOGGER.info(taskPath + " (" + Thread.currentThread() + ") - complete");
-        }
-    }
 }
diff --git a/subprojects/core/src/main/groovy/org/gradle/execution/taskgraph/TaskDependencyGraph.java b/subprojects/core/src/main/groovy/org/gradle/execution/taskgraph/TaskDependencyGraph.java
new file mode 100644
index 0000000..2bf5e7c
--- /dev/null
+++ b/subprojects/core/src/main/groovy/org/gradle/execution/taskgraph/TaskDependencyGraph.java
@@ -0,0 +1,52 @@
+/*
+ * Copyright 2013 the original author or authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.gradle.execution.taskgraph;
+
+
+import org.gradle.api.Nullable;
+import org.gradle.api.Task;
+import org.gradle.api.internal.TaskInternal;
+
+import java.util.HashMap;
+import java.util.Map;
+import java.util.Set;
+
+public class TaskDependencyGraph {
+    private final Map<Task, TaskInfo> nodes = new HashMap<Task, TaskInfo>();
+
+    public Set<Task> getTasks() {
+        return nodes.keySet();
+    }
+
+    @Nullable
+    public TaskInfo getNode(Task task) {
+        return nodes.get(task);
+    }
+
+    public TaskInfo addNode(Task task) {
+        TaskInfo node = nodes.get(task);
+        if (node == null) {
+            node = new TaskInfo((TaskInternal) task);
+            nodes.put(task, node);
+        }
+        return node;
+    }
+
+    public void clear() {
+        nodes.clear();
+    }
+}
diff --git a/subprojects/core/src/main/groovy/org/gradle/execution/taskgraph/TaskExecutionPlan.java b/subprojects/core/src/main/groovy/org/gradle/execution/taskgraph/TaskExecutionPlan.java
index 7020ba2..3a2b9a4 100644
--- a/subprojects/core/src/main/groovy/org/gradle/execution/taskgraph/TaskExecutionPlan.java
+++ b/subprojects/core/src/main/groovy/org/gradle/execution/taskgraph/TaskExecutionPlan.java
@@ -17,7 +17,6 @@
 package org.gradle.execution.taskgraph;
 
 import org.gradle.api.Task;
-import org.gradle.api.specs.Spec;
 
 import java.util.List;
 
@@ -26,15 +25,6 @@ import java.util.List;
  */
 public interface TaskExecutionPlan {
     /**
-     * Provides a ready-to-execute task that matches the specified criteria. A task is ready-to-execute if all of it's dependencies have been completed successfully.
-     * If the next matching task is not ready-to-execute, this method will block until it is ready.
-     * If no tasks remain that match the criteria, null will be returned.
-     * @param criteria Only tasks matching this Spec will be returned.
-     * @return The next matching task, or null if no matching tasks remain.
-     */
-    TaskInfo getTaskToExecute(Spec<TaskInfo> criteria);
-
-    /**
      * Signals to the plan that execution of this task has completed. Execution is complete if the task succeeds, fails, or an exception is thrown during execution.
      * @param task the completed task.
      */
@@ -50,6 +40,11 @@ public interface TaskExecutionPlan {
      */
     List<Task> getTasks();
 
-    //TODO SF this should replace completely getTaskToExecute(), inherit and expand existing unit test coverage
+    /**
+     * Provides a ready-to-execute task. A task is ready-to-execute if all of its dependencies have been completed successfully.
+     * This method blocks until the at least one task is ready-to-execute.
+     * If no tasks remain, null will be returned.
+     * @return The task, or null if no matching tasks remain.
+     */
     TaskInfo getTaskToExecute();
 }
diff --git a/subprojects/core/src/main/groovy/org/gradle/execution/taskgraph/TaskInfo.java b/subprojects/core/src/main/groovy/org/gradle/execution/taskgraph/TaskInfo.java
index ea7b9fb..a362d70 100644
--- a/subprojects/core/src/main/groovy/org/gradle/execution/taskgraph/TaskInfo.java
+++ b/subprojects/core/src/main/groovy/org/gradle/execution/taskgraph/TaskInfo.java
@@ -16,45 +16,67 @@
 
 package org.gradle.execution.taskgraph;
 
+import com.google.common.collect.Iterables;
 import org.gradle.api.internal.TaskInternal;
 
-import java.util.Set;
+import java.util.TreeSet;
 
-class TaskInfo {
+class TaskInfo implements Comparable<TaskInfo> {
 
     private enum TaskExecutionState {
-        READY, EXECUTING, EXECUTED, SKIPPED
+        UNKNOWN, NOT_REQUIRED, SHOULD_RUN, MUST_RUN, MUST_NOT_RUN, EXECUTING, EXECUTED, SKIPPED
     }
 
     private final TaskInternal task;
-    private final Set<TaskInfo> dependencies;
     private TaskExecutionState state;
     private Throwable executionFailure;
+    private boolean dependenciesProcessed;
+    private final TreeSet<TaskInfo> hardPredecessors = new TreeSet<TaskInfo>();
+    private final TreeSet<TaskInfo> hardSuccessors = new TreeSet<TaskInfo>();
+    private final TreeSet<TaskInfo> softSuccessors = new TreeSet<TaskInfo>();
+    private final TreeSet<TaskInfo> finalizers = new TreeSet<TaskInfo>();
 
-    public TaskInfo(TaskInternal task, Set<TaskInfo> dependencies) {
+    public TaskInfo(TaskInternal task) {
         this.task = task;
-        this.dependencies = dependencies;
-        this.state = TaskExecutionState.READY;
+        this.state = TaskExecutionState.UNKNOWN;
     }
 
     public TaskInternal getTask() {
         return task;
     }
 
-    public Set<TaskInfo> getDependencies() {
-        return dependencies;
+    public boolean isRequired() {
+        return state == TaskExecutionState.SHOULD_RUN;
+    }
+
+    public boolean getMustNotRun() {
+        return state == TaskExecutionState.MUST_NOT_RUN;
+    }
+
+    public boolean isIncludeInGraph() {
+        return state == TaskExecutionState.NOT_REQUIRED || state == TaskExecutionState.UNKNOWN;
     }
 
     public boolean isReady() {
-        return state == TaskExecutionState.READY;
+        return state == TaskExecutionState.SHOULD_RUN || state == TaskExecutionState.MUST_RUN;
+    }
+
+    public boolean isInKnownState() {
+        return state != TaskExecutionState.UNKNOWN;
     }
 
     public boolean isComplete() {
-        return state == TaskExecutionState.EXECUTED || state == TaskExecutionState.SKIPPED;
+        return state == TaskExecutionState.EXECUTED
+                || state == TaskExecutionState.SKIPPED
+                || state == TaskExecutionState.UNKNOWN
+                || state == TaskExecutionState.NOT_REQUIRED
+                || state == TaskExecutionState.MUST_NOT_RUN;
     }
 
     public boolean isSuccessful() {
-        return state == TaskExecutionState.EXECUTED && !isFailed();
+        return (state == TaskExecutionState.EXECUTED && !isFailed())
+                || state == TaskExecutionState.NOT_REQUIRED
+                || state == TaskExecutionState.MUST_NOT_RUN;
     }
 
     public boolean isFailed() {
@@ -62,7 +84,7 @@ class TaskInfo {
     }
 
     public void startExecution() {
-        assert state == TaskExecutionState.READY;
+        assert isReady();
         state = TaskExecutionState.EXECUTING;
     }
 
@@ -72,10 +94,27 @@ class TaskInfo {
     }
 
     public void skipExecution() {
-        assert state == TaskExecutionState.READY;
+        assert state == TaskExecutionState.SHOULD_RUN;
         state = TaskExecutionState.SKIPPED;
     }
 
+    public void require() {
+        state = TaskExecutionState.SHOULD_RUN;
+    }
+
+    public void doNotRequire() {
+        state = TaskExecutionState.NOT_REQUIRED;
+    }
+
+    public void mustNotRun() {
+        state = TaskExecutionState.MUST_NOT_RUN;
+    }
+
+    public void enforceRun() {
+        assert state == TaskExecutionState.SHOULD_RUN || state == TaskExecutionState.MUST_NOT_RUN || state == TaskExecutionState.MUST_RUN;
+        state = TaskExecutionState.MUST_RUN;
+    }
+
     public void setExecutionFailure(Throwable failure) {
         assert state == TaskExecutionState.EXECUTING;
         this.executionFailure = failure;
@@ -90,7 +129,7 @@ class TaskInfo {
     }
 
     public boolean allDependenciesComplete() {
-        for (TaskInfo dependency : getDependencies()) {
+        for (TaskInfo dependency : Iterables.concat(softSuccessors, hardSuccessors)) {
             if (!dependency.isComplete()) {
                 return false;
             }
@@ -99,11 +138,52 @@ class TaskInfo {
     }
 
     public boolean allDependenciesSuccessful() {
-        for (TaskInfo dependency : getDependencies()) {
+        for (TaskInfo dependency : hardSuccessors) {
             if (!dependency.isSuccessful()) {
                 return false;
             }
         }
         return true;
     }
+
+    public TreeSet<TaskInfo> getHardPredecessors() {
+        return hardPredecessors;
+    }
+
+    public TreeSet<TaskInfo> getHardSuccessors() {
+        return hardSuccessors;
+    }
+
+    public TreeSet<TaskInfo> getSoftSuccessors() {
+        return softSuccessors;
+    }
+
+    public TreeSet<TaskInfo> getFinalizers() {
+        return finalizers;
+    }
+
+    public boolean getDependenciesProcessed() {
+        return dependenciesProcessed;
+    }
+
+    public void dependenciesProcessed() {
+        dependenciesProcessed = true;
+    }
+
+    public void addHardSuccessor(TaskInfo toNode) {
+        hardSuccessors.add(toNode);
+        toNode.hardPredecessors.add(this);
+    }
+
+    public void addSoftSuccessor(TaskInfo toNode) {
+        softSuccessors.add(toNode);
+    }
+
+    public void addFinalizer(TaskInfo finalizerNode) {
+        finalizers.add(finalizerNode);
+    }
+
+    public int compareTo(TaskInfo otherInfo) {
+        return task.compareTo(otherInfo.getTask());
+    }
 }
diff --git a/subprojects/core/src/main/groovy/org/gradle/execution/taskgraph/TaskPlanExecutorFactory.java b/subprojects/core/src/main/groovy/org/gradle/execution/taskgraph/TaskPlanExecutorFactory.java
index e988f2c..a112103 100644
--- a/subprojects/core/src/main/groovy/org/gradle/execution/taskgraph/TaskPlanExecutorFactory.java
+++ b/subprojects/core/src/main/groovy/org/gradle/execution/taskgraph/TaskPlanExecutorFactory.java
@@ -16,9 +16,8 @@
 
 package org.gradle.execution.taskgraph;
 
-import org.gradle.api.internal.changedetection.TaskArtifactStateCacheAccess;
+import org.gradle.api.internal.changedetection.state.TaskArtifactStateCacheAccess;
 import org.gradle.internal.Factory;
-import org.gradle.util.SingleMessageLogger;
 
 public class TaskPlanExecutorFactory implements Factory<TaskPlanExecutor> {
 
@@ -33,10 +32,8 @@ public class TaskPlanExecutorFactory implements Factory<TaskPlanExecutor> {
     public TaskPlanExecutor create() {
         ExecutionOptions options = new ExecutionOptions(parallelThreads);
         if (options.executeProjectsInParallel()) {
-            SingleMessageLogger.informAboutIncubating("Parallel project execution");
             return new ParallelTaskPlanExecutor(taskArtifactStateCacheAccess, options.numberOfParallelThreads());
         }
-        return new DefaultTaskPlanExecutor();
-
+        return new DefaultTaskPlanExecutor(taskArtifactStateCacheAccess);
     }
 }
diff --git a/subprojects/core/src/main/groovy/org/gradle/execution/taskpath/ProjectFinderByTaskPath.java b/subprojects/core/src/main/groovy/org/gradle/execution/taskpath/ProjectFinderByTaskPath.java
index d6e43ff..50372ff 100644
--- a/subprojects/core/src/main/groovy/org/gradle/execution/taskpath/ProjectFinderByTaskPath.java
+++ b/subprojects/core/src/main/groovy/org/gradle/execution/taskpath/ProjectFinderByTaskPath.java
@@ -23,9 +23,6 @@ import org.gradle.util.NameMatcher;
 
 import java.util.Map;
 
-/**
- * by Szczepan Faber, created at: 1/3/13
- */
 public class ProjectFinderByTaskPath {
 
     public ProjectInternal findProject(String projectPath, ProjectInternal startFrom) {
diff --git a/subprojects/core/src/main/groovy/org/gradle/execution/taskpath/ResolvedTaskPath.java b/subprojects/core/src/main/groovy/org/gradle/execution/taskpath/ResolvedTaskPath.java
index 031ddef..f074bec 100644
--- a/subprojects/core/src/main/groovy/org/gradle/execution/taskpath/ResolvedTaskPath.java
+++ b/subprojects/core/src/main/groovy/org/gradle/execution/taskpath/ResolvedTaskPath.java
@@ -18,9 +18,6 @@ package org.gradle.execution.taskpath;
 
 import org.gradle.api.internal.project.ProjectInternal;
 
-/**
- * by Szczepan Faber, created at: 1/29/13
- */
 public class ResolvedTaskPath {
     private final String prefix;
     private final String taskName;
diff --git a/subprojects/core/src/main/groovy/org/gradle/execution/taskpath/TaskPathResolver.java b/subprojects/core/src/main/groovy/org/gradle/execution/taskpath/TaskPathResolver.java
index a419d55..5d87ad5 100644
--- a/subprojects/core/src/main/groovy/org/gradle/execution/taskpath/TaskPathResolver.java
+++ b/subprojects/core/src/main/groovy/org/gradle/execution/taskpath/TaskPathResolver.java
@@ -19,9 +19,6 @@ package org.gradle.execution.taskpath;
 import org.gradle.api.Project;
 import org.gradle.api.internal.project.ProjectInternal;
 
-/**
- * by Szczepan Faber, created at: 1/29/13
- */
 public class TaskPathResolver {
 
     private final ProjectFinderByTaskPath projectFinder;
diff --git a/subprojects/core/src/main/groovy/org/gradle/groovy/scripts/BasicScript.java b/subprojects/core/src/main/groovy/org/gradle/groovy/scripts/BasicScript.java
index 4db2849..19d99ff 100755
--- a/subprojects/core/src/main/groovy/org/gradle/groovy/scripts/BasicScript.java
+++ b/subprojects/core/src/main/groovy/org/gradle/groovy/scripts/BasicScript.java
@@ -19,17 +19,13 @@ package org.gradle.groovy.scripts;
 import groovy.lang.MetaClass;
 import org.gradle.api.internal.DynamicObject;
 import org.gradle.api.internal.DynamicObjectUtil;
-import org.gradle.internal.service.ServiceRegistry;
+import org.gradle.api.internal.ProcessOperations;
 import org.gradle.api.internal.file.FileOperations;
+import org.gradle.internal.service.ServiceRegistry;
 import org.gradle.logging.StandardOutputCapture;
-import org.gradle.api.internal.ProcessOperations;
 
 import java.util.Map;
 
-/**
- * @author Hans Dockter
- *
- */
 public abstract class BasicScript extends org.gradle.groovy.scripts.Script implements org.gradle.api.Script, FileOperations, ProcessOperations {
     private StandardOutputCapture standardOutputCapture;
     private Object target;
diff --git a/subprojects/core/src/main/groovy/org/gradle/groovy/scripts/DefaultScript.java b/subprojects/core/src/main/groovy/org/gradle/groovy/scripts/DefaultScript.java
index f911362..e56c636 100755
--- a/subprojects/core/src/main/groovy/org/gradle/groovy/scripts/DefaultScript.java
+++ b/subprojects/core/src/main/groovy/org/gradle/groovy/scripts/DefaultScript.java
@@ -17,6 +17,7 @@
 package org.gradle.groovy.scripts;
 
 import groovy.lang.Closure;
+import org.gradle.api.Action;
 import org.gradle.api.PathValidation;
 import org.gradle.api.Script;
 import org.gradle.api.file.ConfigurableFileCollection;
@@ -26,6 +27,7 @@ import org.gradle.api.file.FileTree;
 import org.gradle.api.initialization.dsl.ScriptHandler;
 import org.gradle.api.internal.ProcessOperations;
 import org.gradle.api.internal.file.*;
+import org.gradle.api.internal.file.copy.CopySpecInternal;
 import org.gradle.api.internal.plugins.DefaultObjectConfigurationAction;
 import org.gradle.api.logging.Logger;
 import org.gradle.api.logging.Logging;
@@ -34,6 +36,7 @@ import org.gradle.api.resources.ResourceHandler;
 import org.gradle.api.tasks.WorkResult;
 import org.gradle.configuration.ScriptPluginFactory;
 import org.gradle.internal.nativeplatform.filesystem.FileSystems;
+import org.gradle.internal.reflect.Instantiator;
 import org.gradle.internal.service.ServiceRegistry;
 import org.gradle.process.ExecResult;
 import org.gradle.util.ConfigureUtil;
@@ -54,14 +57,15 @@ public abstract class DefaultScript extends BasicScript {
         super.init(target, services);
         this.services = services;
         loggingManager = services.get(LoggingManager.class);
+        Instantiator instantiator = services.get(Instantiator.class);
         if (target instanceof FileOperations) {
             fileOperations = (FileOperations) target;
         } else if (getScriptSource().getResource().getFile() != null) {
             fileOperations = new DefaultFileOperations(
-                    new BaseDirFileResolver(FileSystems.getDefault(), getScriptSource().getResource().getFile().getParentFile()), null, null
-            );
+                    new BaseDirFileResolver(FileSystems.getDefault(), getScriptSource().getResource().getFile().getParentFile()), null, null,
+                    instantiator);
         } else {
-            fileOperations = new DefaultFileOperations(new IdentityFileResolver(), null, null);
+            fileOperations = new DefaultFileOperations(new IdentityFileResolver(), null, null, instantiator);
         }
 
         processOperations = (ProcessOperations) fileOperations;
@@ -153,10 +157,18 @@ public abstract class DefaultScript extends BasicScript {
         return fileOperations.copy(closure);
     }
 
+    public WorkResult sync(Action<? super CopySpec> action) {
+        return fileOperations.sync(action);
+    }
+
     public CopySpec copySpec(Closure closure) {
         return fileOperations.copySpec(closure);
     }
 
+    public CopySpecInternal copySpec(Action<? super CopySpec> action) {
+        return fileOperations.copySpec(action);
+    }
+
     public File mkdir(Object path) {
         return fileOperations.mkdir(path);
     }
diff --git a/subprojects/core/src/main/groovy/org/gradle/groovy/scripts/DefaultScriptCompilerFactory.java b/subprojects/core/src/main/groovy/org/gradle/groovy/scripts/DefaultScriptCompilerFactory.java
index 62de9c6..fa34188 100644
--- a/subprojects/core/src/main/groovy/org/gradle/groovy/scripts/DefaultScriptCompilerFactory.java
+++ b/subprojects/core/src/main/groovy/org/gradle/groovy/scripts/DefaultScriptCompilerFactory.java
@@ -15,14 +15,11 @@
  */
 package org.gradle.groovy.scripts;
 
-import org.gradle.internal.reflect.DirectInstantiator;
-import org.gradle.internal.reflect.Instantiator;
 import org.gradle.groovy.scripts.internal.ScriptClassCompiler;
 import org.gradle.groovy.scripts.internal.ScriptRunnerFactory;
+import org.gradle.internal.reflect.DirectInstantiator;
+import org.gradle.internal.reflect.Instantiator;
 
-/**
- * @author Hans Dockter
- */
 public class DefaultScriptCompilerFactory implements ScriptCompilerFactory {
     private final ScriptRunnerFactory scriptRunnerFactory;
     private final ScriptClassCompiler scriptClassCompiler;
diff --git a/subprojects/core/src/main/groovy/org/gradle/groovy/scripts/ScriptCompilerFactory.java b/subprojects/core/src/main/groovy/org/gradle/groovy/scripts/ScriptCompilerFactory.java
index ea4fb9c..1c5e396 100644
--- a/subprojects/core/src/main/groovy/org/gradle/groovy/scripts/ScriptCompilerFactory.java
+++ b/subprojects/core/src/main/groovy/org/gradle/groovy/scripts/ScriptCompilerFactory.java
@@ -17,8 +17,6 @@ package org.gradle.groovy.scripts;
 
 /**
  * A factory for script compilers.
- *
- * @author Hans Dockter
  */
 public interface ScriptCompilerFactory {
     /**
diff --git a/subprojects/core/src/main/groovy/org/gradle/groovy/scripts/internal/AsmBackedEmptyScriptGenerator.java b/subprojects/core/src/main/groovy/org/gradle/groovy/scripts/internal/AsmBackedEmptyScriptGenerator.java
index b6c2fbb..afbaed7 100644
--- a/subprojects/core/src/main/groovy/org/gradle/groovy/scripts/internal/AsmBackedEmptyScriptGenerator.java
+++ b/subprojects/core/src/main/groovy/org/gradle/groovy/scripts/internal/AsmBackedEmptyScriptGenerator.java
@@ -16,7 +16,8 @@
 package org.gradle.groovy.scripts.internal;
 
 import groovy.lang.Script;
-import org.gradle.util.ReflectionUtil;
+import org.gradle.internal.reflect.JavaReflectionUtil;
+import org.gradle.util.JavaMethod;
 import org.objectweb.asm.ClassWriter;
 import org.objectweb.asm.MethodVisitor;
 import org.objectweb.asm.Opcodes;
@@ -77,8 +78,9 @@ public class AsmBackedEmptyScriptGenerator implements EmptyScriptGenerator {
         visitor.visitEnd();
 
         byte[] bytecode = visitor.toByteArray();
-        return (Class<T>) ReflectionUtil.invoke(type.getClassLoader(), "defineClass", new Object[]{
-                typeName, bytecode, 0, bytecode.length
-        });
+        JavaMethod<ClassLoader, Class> method = JavaReflectionUtil.method(ClassLoader.class, Class.class, "defineClass", String.class, byte[].class, int.class, int.class);
+        @SuppressWarnings("unchecked")
+        Class<T> clazz = method.invoke(type.getClassLoader(), typeName, bytecode, 0, bytecode.length);
+        return clazz;
     }
 }
diff --git a/subprojects/core/src/main/groovy/org/gradle/groovy/scripts/internal/ClasspathScriptTransformer.java b/subprojects/core/src/main/groovy/org/gradle/groovy/scripts/internal/ClasspathScriptTransformer.java
index efe2952..f1cf200 100644
--- a/subprojects/core/src/main/groovy/org/gradle/groovy/scripts/internal/ClasspathScriptTransformer.java
+++ b/subprojects/core/src/main/groovy/org/gradle/groovy/scripts/internal/ClasspathScriptTransformer.java
@@ -41,7 +41,7 @@ import java.util.Map;
 /**
  * The classpath script transformer uses Groovy's AST support to implement a two-phase
  * compilation of a script into a "class path script" and an "everything else script".
- * The classpath script can then be executed and it's results taken into account (in
+ * The classpath script can then be executed and its results taken into account (in
  * particular, to update the classpath) before the remainder of the script is executed.
  */
 public abstract class ClasspathScriptTransformer extends AbstractScriptTransformer {
@@ -174,4 +174,4 @@ public abstract class ClasspathScriptTransformer extends AbstractScriptTransform
             }
         };
     }
-}
\ No newline at end of file
+}
diff --git a/subprojects/core/src/main/groovy/org/gradle/groovy/scripts/internal/DefaultScriptCompilationHandler.java b/subprojects/core/src/main/groovy/org/gradle/groovy/scripts/internal/DefaultScriptCompilationHandler.java
index 3182a27..3b143e6 100644
--- a/subprojects/core/src/main/groovy/org/gradle/groovy/scripts/internal/DefaultScriptCompilationHandler.java
+++ b/subprojects/core/src/main/groovy/org/gradle/groovy/scripts/internal/DefaultScriptCompilationHandler.java
@@ -44,9 +44,6 @@ import java.net.URLClassLoader;
 import java.security.CodeSource;
 import java.util.List;
 
-/**
- * @author Hans Dockter
- */
 public class DefaultScriptCompilationHandler implements ScriptCompilationHandler {
     private Logger logger = LoggerFactory.getLogger(DefaultScriptCompilationHandler.class);
     private static final String EMPTY_SCRIPT_MARKER_FILE_NAME = "emptyScript.txt";
diff --git a/subprojects/core/src/main/groovy/org/gradle/groovy/scripts/internal/ScriptCompilationHandler.java b/subprojects/core/src/main/groovy/org/gradle/groovy/scripts/internal/ScriptCompilationHandler.java
index afd9584..3642cfc 100644
--- a/subprojects/core/src/main/groovy/org/gradle/groovy/scripts/internal/ScriptCompilationHandler.java
+++ b/subprojects/core/src/main/groovy/org/gradle/groovy/scripts/internal/ScriptCompilationHandler.java
@@ -21,9 +21,6 @@ import org.gradle.groovy.scripts.Transformer;
 
 import java.io.File;
 
-/**
- * @author Hans Dockter
- */
 public interface ScriptCompilationHandler {
     void compileToDir(ScriptSource source, ClassLoader classLoader, File scriptCacheDir, Transformer transformer,
                       Class<? extends Script> scriptBaseClass);
diff --git a/subprojects/core/src/main/groovy/org/gradle/initialization/AbstractProjectSpec.java b/subprojects/core/src/main/groovy/org/gradle/initialization/AbstractProjectSpec.java
index ba20320..adf92f9 100644
--- a/subprojects/core/src/main/groovy/org/gradle/initialization/AbstractProjectSpec.java
+++ b/subprojects/core/src/main/groovy/org/gradle/initialization/AbstractProjectSpec.java
@@ -15,7 +15,7 @@
  */
 package org.gradle.initialization;
 
-import org.gradle.api.internal.project.IProjectRegistry;
+import org.gradle.api.internal.project.ProjectRegistry;
 import org.gradle.api.internal.project.ProjectIdentifier;
 import org.gradle.api.InvalidUserDataException;
 
@@ -23,7 +23,7 @@ import java.util.List;
 import java.util.ArrayList;
 
 public abstract class AbstractProjectSpec implements ProjectSpec {
-    public boolean containsProject(IProjectRegistry<?> registry) {
+    public boolean containsProject(ProjectRegistry<?> registry) {
         checkPreconditions(registry);
         for (ProjectIdentifier project : registry.getAllProjects()) {
             if (select(project)) {
@@ -33,7 +33,7 @@ public abstract class AbstractProjectSpec implements ProjectSpec {
         return false;
     }
 
-    public <T extends ProjectIdentifier> T selectProject(IProjectRegistry<? extends T> registry) {
+    public <T extends ProjectIdentifier> T selectProject(ProjectRegistry<? extends T> registry) {
         checkPreconditions(registry);
         List<T> matches = new ArrayList<T>();
         for (T project : registry.getAllProjects()) {
@@ -50,7 +50,7 @@ public abstract class AbstractProjectSpec implements ProjectSpec {
         return matches.get(0);
     }
 
-    protected void checkPreconditions(IProjectRegistry<?> registry) {
+    protected void checkPreconditions(ProjectRegistry<?> registry) {
     }
 
     protected abstract String formatMultipleMatchesMessage(Iterable<? extends ProjectIdentifier> matches);
diff --git a/subprojects/core/src/main/groovy/org/gradle/initialization/BaseSettings.java b/subprojects/core/src/main/groovy/org/gradle/initialization/BaseSettings.java
index 1aa27da..6b3e273 100644
--- a/subprojects/core/src/main/groovy/org/gradle/initialization/BaseSettings.java
+++ b/subprojects/core/src/main/groovy/org/gradle/initialization/BaseSettings.java
@@ -21,44 +21,54 @@ import org.gradle.api.initialization.ProjectDescriptor;
 import org.gradle.api.initialization.Settings;
 import org.gradle.api.internal.GradleInternal;
 import org.gradle.api.internal.SettingsInternal;
-import org.gradle.api.internal.project.IProjectRegistry;
+import org.gradle.api.internal.file.FileResolver;
+import org.gradle.api.internal.project.AbstractPluginAware;
+import org.gradle.api.internal.project.ProjectRegistry;
+import org.gradle.internal.service.scopes.ServiceRegistryFactory;
+import org.gradle.api.plugins.PluginContainer;
+import org.gradle.configuration.ScriptPluginFactory;
 import org.gradle.groovy.scripts.ScriptSource;
 
 import java.io.File;
-import java.net.URLClassLoader;
 
-/**
- * @author Hans Dockter
- */
-public class BaseSettings implements SettingsInternal {
+public class BaseSettings extends AbstractPluginAware implements SettingsInternal {
     public static final String DEFAULT_BUILD_SRC_DIR = "buildSrc";
 
     private ScriptSource settingsScript;
 
     private StartParameter startParameter;
 
-    private URLClassLoader classloader;
+    private ClassLoader classloader;
 
     private File settingsDir;
 
     private DefaultProjectDescriptor rootProjectDescriptor;
 
     private GradleInternal gradle;
-    private IProjectDescriptorRegistry projectDescriptorRegistry;
 
-    protected BaseSettings() {
-    }
+    private ProjectDescriptorRegistry projectDescriptorRegistry;
+
+    private  ServiceRegistryFactory services;
+
+    private PluginContainer plugins;
 
-    public BaseSettings(GradleInternal gradle,
-                        IProjectDescriptorRegistry projectDescriptorRegistry,
-                        URLClassLoader classloader, File settingsDir, ScriptSource settingsScript,
+    private FileResolver fileResolver;
+
+    private ScriptPluginFactory scriptPluginFactory;
+
+    public BaseSettings(ServiceRegistryFactory serviceRegistryFactory, GradleInternal gradle,
+                        ClassLoader classloader, File settingsDir, ScriptSource settingsScript,
                         StartParameter startParameter) {
         this.gradle = gradle;
-        this.projectDescriptorRegistry = projectDescriptorRegistry;
         this.settingsDir = settingsDir;
         this.settingsScript = settingsScript;
         this.startParameter = startParameter;
         this.classloader = classloader;
+        this.services = serviceRegistryFactory.createFor(this);
+        this.plugins = services.get(PluginContainer.class);
+        this.fileResolver = services.get(FileResolver.class);
+        this.scriptPluginFactory = services.get(ScriptPluginFactory.class);
+        this.projectDescriptorRegistry = services.get(ProjectDescriptorRegistry.class);
         rootProjectDescriptor = createProjectDescriptor(null, settingsDir.getName(), settingsDir);
     }
 
@@ -76,7 +86,7 @@ public class BaseSettings implements SettingsInternal {
     }
 
     public DefaultProjectDescriptor createProjectDescriptor(DefaultProjectDescriptor parent, String name, File dir) {
-        return new DefaultProjectDescriptor(parent, name, dir, projectDescriptorRegistry);
+        return new DefaultProjectDescriptor(parent, name, dir, projectDescriptorRegistry, fileResolver);
     }
 
     public DefaultProjectDescriptor findProject(String path) {
@@ -134,7 +144,7 @@ public class BaseSettings implements SettingsInternal {
         return projectPath;
     }
 
-    public URLClassLoader getClassLoader() {
+    public ClassLoader getClassLoader() {
         return classloader;
     }
 
@@ -174,15 +184,30 @@ public class BaseSettings implements SettingsInternal {
         this.settingsScript = settingsScript;
     }
 
-    public IProjectDescriptorRegistry getProjectDescriptorRegistry() {
+    public ProjectDescriptorRegistry getProjectDescriptorRegistry() {
         return projectDescriptorRegistry;
     }
 
-    public void setProjectDescriptorRegistry(IProjectDescriptorRegistry projectDescriptorRegistry) {
+    public void setProjectDescriptorRegistry(ProjectDescriptorRegistry projectDescriptorRegistry) {
         this.projectDescriptorRegistry = projectDescriptorRegistry;
     }
 
-    public IProjectRegistry<DefaultProjectDescriptor> getProjectRegistry() {
+    public ProjectRegistry<DefaultProjectDescriptor> getProjectRegistry() {
         return projectDescriptorRegistry;
     }
+
+    public PluginContainer getPlugins() {
+        return plugins;
+    }
+
+
+    @Override
+    protected FileResolver getFileResolver() {
+        return fileResolver;
+    }
+
+    @Override
+    protected ScriptPluginFactory getScriptPluginFactory() {
+        return scriptPluginFactory;
+    }
 }
diff --git a/subprojects/core/src/main/groovy/org/gradle/initialization/BuildAction.java b/subprojects/core/src/main/groovy/org/gradle/initialization/BuildAction.java
new file mode 100644
index 0000000..258e0ee
--- /dev/null
+++ b/subprojects/core/src/main/groovy/org/gradle/initialization/BuildAction.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.initialization;
+
+/**
+ * An object that performs some action with a {@link BuildController} and produces a “result” object (e.g. the output).
+ * <p>
+ * Implementations of this are typically composed to bootstrap a build in a certain environment.
+ * <p>
+ */
+public interface BuildAction<T> {
+    /**
+     * Executes the action with the given controller.
+     * <p>
+     * The state of the build is not defined as part of this contract, it is highly context specific.
+     */
+    T run(BuildController buildController);
+}
diff --git a/subprojects/core/src/main/groovy/org/gradle/initialization/BuildController.java b/subprojects/core/src/main/groovy/org/gradle/initialization/BuildController.java
new file mode 100644
index 0000000..f74f6ea
--- /dev/null
+++ b/subprojects/core/src/main/groovy/org/gradle/initialization/BuildController.java
@@ -0,0 +1,45 @@
+/*
+ * Copyright 2012 the original author or authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS 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.GradleLauncher;
+import org.gradle.StartParameter;
+
+/**
+ * This is intended to eventually replace {@link GradleLauncher} internally. It's pretty rough at the moment.
+ */
+public interface BuildController {
+    /**
+     * Specifies the start parameter to use to run the build. Cannot be used after the launcher has been created.
+     */
+    void setStartParameter(StartParameter startParameter);
+
+    /**
+     * Returns the launcher to use to run the build.
+     */
+    GradleLauncher getLauncher();
+
+    /**
+     * Runs the build.
+     */
+    void run();
+
+    /**
+     * Configures the build but does not run any tasks.
+     */
+    void configure();
+}
diff --git a/subprojects/core/src/main/groovy/org/gradle/initialization/BuildFileProjectSpec.java b/subprojects/core/src/main/groovy/org/gradle/initialization/BuildFileProjectSpec.java
index e291545..3058929 100644
--- a/subprojects/core/src/main/groovy/org/gradle/initialization/BuildFileProjectSpec.java
+++ b/subprojects/core/src/main/groovy/org/gradle/initialization/BuildFileProjectSpec.java
@@ -18,7 +18,7 @@ package org.gradle.initialization;
 import org.apache.commons.lang.builder.EqualsBuilder;
 import org.apache.commons.lang.builder.HashCodeBuilder;
 import org.gradle.api.internal.project.ProjectIdentifier;
-import org.gradle.api.internal.project.IProjectRegistry;
+import org.gradle.api.internal.project.ProjectRegistry;
 import org.gradle.api.InvalidUserDataException;
 
 import java.io.File;
@@ -48,7 +48,7 @@ public class BuildFileProjectSpec extends AbstractProjectSpec implements Seriali
     }
 
     @Override
-    protected void checkPreconditions(IProjectRegistry<?> registry) {
+    protected void checkPreconditions(ProjectRegistry<?> registry) {
         if (!buildFile.exists()) {
             throw new InvalidUserDataException(String.format("Build file '%s' does not exist.", buildFile));
         }
diff --git a/subprojects/core/src/main/groovy/org/gradle/initialization/BuildLayoutParameters.java b/subprojects/core/src/main/groovy/org/gradle/initialization/BuildLayoutParameters.java
index 9f32a3c..a8d0d81 100644
--- a/subprojects/core/src/main/groovy/org/gradle/initialization/BuildLayoutParameters.java
+++ b/subprojects/core/src/main/groovy/org/gradle/initialization/BuildLayoutParameters.java
@@ -16,17 +16,30 @@
 
 package org.gradle.initialization;
 
+import org.gradle.StartParameter;
+import org.gradle.internal.SystemProperties;
+
 import java.io.File;
 
-/**
- * by Szczepan Faber, created at: 2/18/13
- */
+import static org.gradle.util.GFileUtils.canonicalise;
+
 public class BuildLayoutParameters {
 
-    private Boolean searchUpwards;
-    private File projectDir;
+    private boolean searchUpwards = true;
+    private File projectDir = canonicalise(SystemProperties.getCurrentDir());
     private File gradleUserHomeDir;
 
+    public BuildLayoutParameters() {
+        String gradleUserHome = System.getProperty(StartParameter.GRADLE_USER_HOME_PROPERTY_KEY);
+        if (gradleUserHome == null) {
+            gradleUserHome = System.getenv("GRADLE_USER_HOME");
+            if (gradleUserHome == null) {
+                gradleUserHome = StartParameter.DEFAULT_GRADLE_USER_HOME.getAbsolutePath();
+            }
+        }
+        gradleUserHomeDir = canonicalise(new File(gradleUserHome));
+    }
+
     public BuildLayoutParameters setSearchUpwards(boolean searchUpwards) {
         this.searchUpwards = searchUpwards;
         return this;
@@ -50,7 +63,7 @@ public class BuildLayoutParameters {
         return gradleUserHomeDir;
     }
 
-    public Boolean getSearchUpwards() {
+    public boolean getSearchUpwards() {
         return searchUpwards;
     }
 }
diff --git a/subprojects/core/src/main/groovy/org/gradle/initialization/BuildSourceBuilder.java b/subprojects/core/src/main/groovy/org/gradle/initialization/BuildSourceBuilder.java
deleted file mode 100644
index d118ae6..0000000
--- a/subprojects/core/src/main/groovy/org/gradle/initialization/BuildSourceBuilder.java
+++ /dev/null
@@ -1,155 +0,0 @@
-/*
- * Copyright 2010 the original author or authors.
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- *      http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package org.gradle.initialization;
-
-import org.gradle.BuildAdapter;
-import org.gradle.GradleLauncher;
-import org.gradle.StartParameter;
-import org.gradle.api.UncheckedIOException;
-import org.gradle.api.internal.GradleInternal;
-import org.gradle.api.internal.plugins.EmbeddableJavaProject;
-import org.gradle.api.invocation.Gradle;
-import org.gradle.cache.CacheBuilder;
-import org.gradle.cache.CacheRepository;
-import org.gradle.cache.PersistentCache;
-import org.gradle.cache.internal.FileLockManager;
-import org.gradle.internal.Factory;
-import org.gradle.internal.classpath.ClassPath;
-import org.gradle.internal.classpath.DefaultClassPath;
-import org.gradle.util.WrapUtil;
-import org.slf4j.Logger;
-import org.slf4j.LoggerFactory;
-
-import java.io.File;
-import java.io.IOException;
-import java.net.URL;
-import java.net.URLClassLoader;
-import java.util.Collection;
-import java.util.Set;
-
-/**
- * @author Hans Dockter
- */
-public class BuildSourceBuilder {
-    private static final Logger LOGGER = LoggerFactory.getLogger(BuildSourceBuilder.class);
-
-    private final GradleLauncherFactory gradleLauncherFactory;
-    private final ClassLoaderRegistry classLoaderRegistry;
-    private final CacheRepository cacheRepository;
-
-    private static final String DEFAULT_BUILD_SOURCE_SCRIPT_RESOURCE = "defaultBuildSourceScript.txt";
-
-    public BuildSourceBuilder(GradleLauncherFactory gradleLauncherFactory, ClassLoaderRegistry classLoaderRegistry, CacheRepository cacheRepository) {
-        this.gradleLauncherFactory = gradleLauncherFactory;
-        this.classLoaderRegistry = classLoaderRegistry;
-        this.cacheRepository = cacheRepository;
-    }
-
-    public URLClassLoader buildAndCreateClassLoader(StartParameter startParameter) {
-        ClassPath classpath = createBuildSourceClasspath(startParameter);
-        return new URLClassLoader(classpath.getAsURLArray(), classLoaderRegistry.getRootClassLoader());
-    }
-
-    private ClassPath createBuildSourceClasspath(StartParameter startParameter) {
-        assert startParameter.getCurrentDir() != null && startParameter.getBuildFile() == null;
-
-        LOGGER.debug("Starting to build the build sources.");
-        if (!startParameter.getCurrentDir().isDirectory()) {
-            LOGGER.debug("Gradle source dir does not exist. We leave.");
-            return new DefaultClassPath();
-        }
-        LOGGER.info("================================================" + " Start building buildSrc");
-
-        // If we were not the most recent version of Gradle to build the buildSrc dir, then do a clean build
-        // Otherwise, just to a regular build
-        final PersistentCache buildSrcCache = cacheRepository.
-                cache("buildSrc").
-                withLockMode(FileLockManager.LockMode.None).
-                forObject(startParameter.getCurrentDir()).
-                withVersionStrategy(CacheBuilder.VersionStrategy.SharedCacheInvalidateOnVersionChange).
-                open();
-
-        GradleLauncher gradleLauncher = buildGradleLauncher(startParameter);
-        return buildSrcCache.useCache("rebuild buildSrc", new BuildSrcUpdateFactory(buildSrcCache, gradleLauncher));
-    }
-
-    private GradleLauncher buildGradleLauncher(StartParameter startParameter) {
-        final StartParameter startParameterArg = startParameter.newInstance();
-        startParameterArg.setProjectProperties(startParameter.getProjectProperties());
-        startParameterArg.setSearchUpwards(false);
-        startParameterArg.setProfile(startParameter.isProfile());
-        return gradleLauncherFactory.newInstance(startParameterArg);
-    }
-
-    static URL getDefaultScript() {
-        return BuildSourceBuilder.class.getResource(DEFAULT_BUILD_SOURCE_SCRIPT_RESOURCE);
-    }
-
-    private static class BuildSrcBuildListener extends BuildAdapter implements ModelConfigurationListener {
-        private EmbeddableJavaProject projectInfo;
-        private Set<File> classpath;
-        private final boolean rebuild;
-
-        public BuildSrcBuildListener(boolean rebuild) {
-            this.rebuild = rebuild;
-        }
-
-        @Override
-        public void projectsLoaded(Gradle gradle) {
-            gradle.getRootProject().apply(WrapUtil.toMap("from", getDefaultScript()));
-        }
-
-        public Collection<File> getRuntimeClasspath() {
-            return classpath;
-        }
-
-        public void onConfigure(GradleInternal gradle) {
-            projectInfo = gradle.getRootProject().getConvention().getPlugin(EmbeddableJavaProject.class);
-            gradle.getStartParameter().setTaskNames(rebuild ? projectInfo.getRebuildTasks() : projectInfo.getBuildTasks());
-            classpath = projectInfo.getRuntimeClasspath().getFiles();
-        }
-    }
-
-    private static class BuildSrcUpdateFactory implements Factory<DefaultClassPath> {
-        private final PersistentCache cache;
-        private final GradleLauncher gradleLauncher;
-
-        public BuildSrcUpdateFactory(PersistentCache cache, GradleLauncher gradleLauncher) {
-            this.cache = cache;
-            this.gradleLauncher = gradleLauncher;
-        }
-
-        public DefaultClassPath create() {
-            File markerFile = new File(cache.getBaseDir(), "built.bin");
-            final boolean rebuild = !markerFile.exists();
-
-            BuildSrcBuildListener listener = new BuildSrcBuildListener(rebuild);
-            gradleLauncher.addListener(listener);
-            gradleLauncher.run().rethrowFailure();
-
-            Collection<File> classpath = listener.getRuntimeClasspath();
-            LOGGER.debug("Gradle source classpath is: {}", classpath);
-            LOGGER.info("================================================" + " Finished building buildSrc");
-            try {
-                markerFile.createNewFile();
-            } catch (IOException e) {
-                throw new UncheckedIOException(e);
-            }
-            return new DefaultClassPath(classpath);
-        }
-    }
-}
diff --git a/subprojects/core/src/main/groovy/org/gradle/initialization/ClassLoaderRegistry.java b/subprojects/core/src/main/groovy/org/gradle/initialization/ClassLoaderRegistry.java
index bf1ca5a..e638ee6 100755
--- a/subprojects/core/src/main/groovy/org/gradle/initialization/ClassLoaderRegistry.java
+++ b/subprojects/core/src/main/groovy/org/gradle/initialization/ClassLoaderRegistry.java
@@ -15,13 +15,16 @@
  */
 package org.gradle.initialization;
 
-import org.gradle.util.MultiParentClassLoader;
-
 public interface ClassLoaderRegistry {
     /**
-     * Returns the root class loader shared by all builds.
+     * Returns the root class loader shared by all builds. This class loader exposes the Gradle API and built-in plugins.
+     */
+    ClassLoader getGradleApiClassLoader();
+
+    /**
+     * Returns the class loader for the Gradle runtime.
      */
-    ClassLoader getRootClassLoader();
+    ClassLoader getRuntimeClassLoader();
 
     /**
      * Returns the class loader for the coreImpl project.
@@ -32,9 +35,4 @@ public interface ClassLoaderRegistry {
      * Returns the class loader for the plugins.
      */
     ClassLoader getPluginsClassLoader();
-
-    /**
-     * Creates the script class loader for a build.
-     */
-    MultiParentClassLoader createScriptClassLoader();
 }
diff --git a/subprojects/core/src/main/groovy/org/gradle/initialization/DefaultClassLoaderRegistry.java b/subprojects/core/src/main/groovy/org/gradle/initialization/DefaultClassLoaderRegistry.java
index eac333d..3f3ab98 100755
--- a/subprojects/core/src/main/groovy/org/gradle/initialization/DefaultClassLoaderRegistry.java
+++ b/subprojects/core/src/main/groovy/org/gradle/initialization/DefaultClassLoaderRegistry.java
@@ -17,10 +17,10 @@
 package org.gradle.initialization;
 
 import org.gradle.api.internal.ClassPathRegistry;
+import org.gradle.internal.classloader.*;
 import org.gradle.internal.classpath.ClassPath;
 import org.gradle.internal.classpath.DefaultClassPath;
 import org.gradle.internal.jvm.Jvm;
-import org.gradle.util.*;
 
 import java.io.File;
 import java.net.URLClassLoader;
@@ -46,7 +46,7 @@ public class DefaultClassLoaderRegistry implements ClassLoaderRegistry {
 
         // Add in libs for plugins
         ClassPath pluginsClassPath = classPathRegistry.getClassPath("GRADLE_PLUGINS");
-        MultiParentClassLoader pluginsImports = new MultiParentClassLoader(runtimeClassLoader, coreImplClassLoader);
+        ClassLoader pluginsImports = new CachingClassLoader(new MultiParentClassLoader(runtimeClassLoader, coreImplClassLoader));
         pluginsClassLoader = new MutableURLClassLoader(pluginsImports, pluginsClassPath);
 
         rootClassLoader = classLoaderFactory.createFilteringClassLoader(pluginsClassLoader);
@@ -63,7 +63,11 @@ public class DefaultClassLoaderRegistry implements ClassLoaderRegistry {
         rootClassLoader.allowPackage("javax.inject");
     }
 
-    public ClassLoader getRootClassLoader() {
+    public ClassLoader getRuntimeClassLoader() {
+        return getClass().getClassLoader();
+    }
+
+    public ClassLoader getGradleApiClassLoader() {
         return rootClassLoader;
     }
 
@@ -74,8 +78,4 @@ public class DefaultClassLoaderRegistry implements ClassLoaderRegistry {
     public ClassLoader getPluginsClassLoader() {
         return pluginsClassLoader;
     }
-
-    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 0be7a9a..5c5d88a 100644
--- a/subprojects/core/src/main/groovy/org/gradle/initialization/DefaultCommandLineConverter.java
+++ b/subprojects/core/src/main/groovy/org/gradle/initialization/DefaultCommandLineConverter.java
@@ -28,9 +28,8 @@ import org.gradle.logging.internal.LoggingCommandLineConverter;
 
 import java.util.Map;
 
-/**
- * @author Hans Dockter
- */
+import static org.gradle.StartParameter.GRADLE_USER_HOME_PROPERTY_KEY;
+
 public class DefaultCommandLineConverter extends AbstractCommandLineConverter<StartParameter> {
     private static final String NO_PROJECT_DEPENDENCY_REBUILD = "a";
     private static final String BUILD_FILE = "b";
@@ -103,17 +102,13 @@ public class DefaultCommandLineConverter extends AbstractCommandLineConverter<St
         Map<String, String> projectProperties = projectPropertiesCommandLineConverter.convert(options);
         startParameter.getProjectProperties().putAll(projectProperties);
 
-        BuildLayoutParameters layout = new BuildLayoutParameters().setProjectDir(startParameter.getCurrentDir());
+        BuildLayoutParameters layout = new BuildLayoutParameters()
+                .setGradleUserHomeDir(startParameter.getGradleUserHomeDir())
+                .setProjectDir(startParameter.getCurrentDir());
         layoutCommandLineConverter.convert(options, layout);
-        if (layout.getGradleUserHomeDir() != null) {
-            startParameter.setGradleUserHomeDir(layout.getGradleUserHomeDir());
-        }
-        if (layout.getProjectDir() != null) {
-            startParameter.setProjectDir(layout.getProjectDir());
-        }
-        if (layout.getSearchUpwards() != null) {
-            startParameter.setSearchUpwards(layout.getSearchUpwards());
-        }
+        startParameter.setGradleUserHomeDir(layout.getGradleUserHomeDir());
+        startParameter.setProjectDir(layout.getProjectDir());
+        startParameter.setSearchUpwards(layout.getSearchUpwards());
 
         if (options.hasOption(BUILD_FILE)) {
             startParameter.setBuildFile(resolver.resolve(options.option(BUILD_FILE).getValue()));
@@ -208,9 +203,16 @@ public class DefaultCommandLineConverter extends AbstractCommandLineConverter<St
 
     void convertCommandLineSystemProperties(Map<String, String> systemProperties, StartParameter startParameter, FileResolver resolver) {
         startParameter.getSystemPropertiesArgs().putAll(systemProperties);
-        String gradleUserHomeProp = "gradle.user.home";
-        if (systemProperties.containsKey(gradleUserHomeProp)) {
-            startParameter.setGradleUserHomeDir(resolver.resolve(systemProperties.get(gradleUserHomeProp)));
+        if (systemProperties.containsKey(GRADLE_USER_HOME_PROPERTY_KEY)) {
+            startParameter.setGradleUserHomeDir(resolver.resolve(systemProperties.get(GRADLE_USER_HOME_PROPERTY_KEY)));
         }
     }
+
+    public LayoutCommandLineConverter getLayoutConverter() {
+        return layoutCommandLineConverter;
+    }
+
+    public SystemPropertiesCommandLineConverter getSystemPropertiesConverter() {
+        return systemPropertiesCommandLineConverter;
+    }
 }
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 c853773..c12f534 100644
--- a/subprojects/core/src/main/groovy/org/gradle/initialization/DefaultGradleLauncher.java
+++ b/subprojects/core/src/main/groovy/org/gradle/initialization/DefaultGradleLauncher.java
@@ -46,7 +46,7 @@ public class DefaultGradleLauncher extends GradleLauncher {
 
     /**
      * Creates a new instance.  Don't call this directly, use {@link #newInstance(org.gradle.StartParameter)} or {@link
-     * #newInstance(String...)} instead.  Note that this method is package-protected to discourage it's direct use.
+     * #newInstance(String...)} instead.
      */
     public DefaultGradleLauncher(GradleInternal gradle, InitScriptHandler initScriptHandler, SettingsHandler settingsHandler,
                                  BuildLoader buildLoader, BuildConfigurer buildConfigurer, BuildListener buildListener,
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 088fb61..e12e63c 100644
--- a/subprojects/core/src/main/groovy/org/gradle/initialization/DefaultGradleLauncherFactory.java
+++ b/subprojects/core/src/main/groovy/org/gradle/initialization/DefaultGradleLauncherFactory.java
@@ -19,15 +19,16 @@ package org.gradle.initialization;
 import org.gradle.*;
 import org.gradle.api.internal.ExceptionAnalyser;
 import org.gradle.api.internal.GradleInternal;
-import org.gradle.api.internal.project.GlobalServicesRegistry;
-import org.gradle.api.internal.project.TopLevelBuildServiceRegistry;
+import org.gradle.internal.service.scopes.BuildScopeServices;
 import org.gradle.api.logging.Logging;
 import org.gradle.api.logging.StandardOutputListener;
 import org.gradle.cache.CacheRepository;
 import org.gradle.cli.CommandLineConverter;
 import org.gradle.configuration.BuildConfigurer;
 import org.gradle.execution.BuildExecuter;
+import org.gradle.initialization.buildsrc.BuildSourceBuilder;
 import org.gradle.initialization.layout.BuildLayoutFactory;
+import org.gradle.internal.featurelifecycle.ScriptUsageLocationReporter;
 import org.gradle.internal.reflect.Instantiator;
 import org.gradle.internal.service.ServiceRegistry;
 import org.gradle.invocation.DefaultGradle;
@@ -37,26 +38,16 @@ import org.gradle.logging.ProgressLoggerFactory;
 import org.gradle.logging.StyledTextOutputFactory;
 import org.gradle.profile.ProfileEventAdapter;
 import org.gradle.profile.ReportGeneratingProfileListener;
+import org.gradle.util.DeprecationLogger;
 
 import java.util.Arrays;
 
-/**
- * @author Hans Dockter
- */
 public class DefaultGradleLauncherFactory implements GradleLauncherFactory {
     private final ServiceRegistry sharedServices;
     private final NestedBuildTracker tracker;
     private CommandLineConverter<StartParameter> commandLineConverter;
 
-    public DefaultGradleLauncherFactory(ServiceRegistry loggingServices) {
-        this(new GlobalServicesRegistry(loggingServices));
-    }
-    
-    public DefaultGradleLauncherFactory() {
-        this(new GlobalServicesRegistry());
-    }
-
-    private DefaultGradleLauncherFactory(GlobalServicesRegistry globalServices) {
+    public DefaultGradleLauncherFactory(ServiceRegistry globalServices) {
         sharedServices = globalServices;
         tracker = new NestedBuildTracker();
 
@@ -100,7 +91,7 @@ public class DefaultGradleLauncherFactory implements GradleLauncherFactory {
     }
 
     private DefaultGradleLauncher doNewInstance(StartParameter startParameter, BuildRequestMetaData requestMetaData) {
-        final TopLevelBuildServiceRegistry serviceRegistry = new TopLevelBuildServiceRegistry(sharedServices, startParameter);
+        final BuildScopeServices serviceRegistry = new BuildScopeServices(sharedServices, startParameter);
         serviceRegistry.add(BuildRequestMetaData.class, requestMetaData);
         serviceRegistry.add(BuildClientMetaData.class, requestMetaData.getClient());
         ListenerManager listenerManager = serviceRegistry.get(ListenerManager.class);
@@ -122,6 +113,9 @@ public class DefaultGradleLauncherFactory implements GradleLauncherFactory {
         if (startParameter.isProfile()) {
             listenerManager.addListener(new ReportGeneratingProfileListener());
         }
+        ScriptUsageLocationReporter usageLocationReporter = new ScriptUsageLocationReporter();
+        listenerManager.addListener(usageLocationReporter);
+        DeprecationLogger.useLocationReporter(usageLocationReporter);
 
         GradleInternal gradle = serviceRegistry.get(Instantiator.class).newInstance(DefaultGradle.class, tracker.getCurrentBuild(), startParameter, serviceRegistry);
         return new DefaultGradleLauncher(
@@ -151,9 +145,9 @@ public class DefaultGradleLauncherFactory implements GradleLauncherFactory {
     }
 
     private static class BuildCleanupListener extends BuildAdapter {
-        private final TopLevelBuildServiceRegistry services;
+        private final BuildScopeServices services;
 
-        private BuildCleanupListener(TopLevelBuildServiceRegistry services) {
+        private BuildCleanupListener(BuildScopeServices services) {
             this.services = services;
         }
 
diff --git a/subprojects/core/src/main/groovy/org/gradle/initialization/DefaultGradlePropertiesLoader.java b/subprojects/core/src/main/groovy/org/gradle/initialization/DefaultGradlePropertiesLoader.java
index bf360d8..62298a2 100644
--- a/subprojects/core/src/main/groovy/org/gradle/initialization/DefaultGradlePropertiesLoader.java
+++ b/subprojects/core/src/main/groovy/org/gradle/initialization/DefaultGradlePropertiesLoader.java
@@ -26,9 +26,6 @@ import java.util.HashMap;
 import java.util.Map;
 import java.util.Properties;
 
-/**
- * @author Hans Dockter
- */
 public class DefaultGradlePropertiesLoader implements IGradlePropertiesLoader {
     private static Logger logger = LoggerFactory.getLogger(DefaultGradlePropertiesLoader.class);
 
diff --git a/subprojects/core/src/main/groovy/org/gradle/initialization/DefaultProjectDescriptor.java b/subprojects/core/src/main/groovy/org/gradle/initialization/DefaultProjectDescriptor.java
index 1a33d47..549f555 100644
--- a/subprojects/core/src/main/groovy/org/gradle/initialization/DefaultProjectDescriptor.java
+++ b/subprojects/core/src/main/groovy/org/gradle/initialization/DefaultProjectDescriptor.java
@@ -17,6 +17,7 @@ package org.gradle.initialization;
 
 import org.gradle.api.Project;
 import org.gradle.api.initialization.ProjectDescriptor;
+import org.gradle.api.internal.file.FileResolver;
 import org.gradle.api.internal.project.ProjectIdentifier;
 import org.gradle.util.GFileUtils;
 import org.gradle.util.Path;
@@ -25,22 +26,21 @@ import java.io.File;
 import java.util.LinkedHashSet;
 import java.util.Set;
 
-/**
- * @author Hans Dockter
- */
 public class DefaultProjectDescriptor implements ProjectDescriptor, ProjectIdentifier {
     private String name;
+    private final FileResolver fileResolver;
     private File dir;
     private DefaultProjectDescriptor parent;
     private Set<ProjectDescriptor> children = new LinkedHashSet<ProjectDescriptor>();
-    private IProjectDescriptorRegistry projectDescriptorRegistry;
+    private ProjectDescriptorRegistry projectDescriptorRegistry;
     private Path path;
     private String buildFileName = Project.DEFAULT_BUILD_FILE;
 
     public DefaultProjectDescriptor(DefaultProjectDescriptor parent, String name, File dir,
-                                    IProjectDescriptorRegistry projectDescriptorRegistry) {
+                                    ProjectDescriptorRegistry projectDescriptorRegistry, FileResolver fileResolver) {
         this.parent = parent;
         this.name = name;
+        this.fileResolver = fileResolver;
         this.dir = GFileUtils.canonicalise(dir);
         this.projectDescriptorRegistry = projectDescriptorRegistry;
         this.path = path(name);
@@ -80,7 +80,7 @@ public class DefaultProjectDescriptor implements ProjectDescriptor, ProjectIdent
     }
 
     public void setProjectDir(File dir) {
-        this.dir = GFileUtils.canonicalise(dir);
+        this.dir = fileResolver.resolve(dir);
     }
 
     public DefaultProjectDescriptor getParent() {
@@ -115,11 +115,11 @@ public class DefaultProjectDescriptor implements ProjectDescriptor, ProjectIdent
         return GFileUtils.canonicalise(new File(dir, buildFileName));
     }
 
-    public IProjectDescriptorRegistry getProjectDescriptorRegistry() {
+    public ProjectDescriptorRegistry getProjectDescriptorRegistry() {
         return projectDescriptorRegistry;
     }
 
-    public void setProjectDescriptorRegistry(IProjectDescriptorRegistry projectDescriptorRegistry) {
+    public void setProjectDescriptorRegistry(ProjectDescriptorRegistry projectDescriptorRegistry) {
         this.projectDescriptorRegistry = projectDescriptorRegistry;
     }
 
diff --git a/subprojects/core/src/main/groovy/org/gradle/initialization/DefaultProjectDescriptorRegistry.java b/subprojects/core/src/main/groovy/org/gradle/initialization/DefaultProjectDescriptorRegistry.java
index 148e100..cd3f64a 100644
--- a/subprojects/core/src/main/groovy/org/gradle/initialization/DefaultProjectDescriptorRegistry.java
+++ b/subprojects/core/src/main/groovy/org/gradle/initialization/DefaultProjectDescriptorRegistry.java
@@ -18,10 +18,7 @@ package org.gradle.initialization;
 import org.gradle.api.internal.project.DefaultProjectRegistry;
 import org.gradle.util.Path;
 
-/**
- * @author Hans Dockter
- */
-public class DefaultProjectDescriptorRegistry extends DefaultProjectRegistry<DefaultProjectDescriptor> implements IProjectDescriptorRegistry {
+public class DefaultProjectDescriptorRegistry extends DefaultProjectRegistry<DefaultProjectDescriptor> implements ProjectDescriptorRegistry {
 
     public void changeDescriptorPath(Path oldPath, Path newPath) {
         DefaultProjectDescriptor projectDescriptor = removeProject(oldPath.toString());
diff --git a/subprojects/core/src/main/groovy/org/gradle/initialization/DefaultProjectSpec.java b/subprojects/core/src/main/groovy/org/gradle/initialization/DefaultProjectSpec.java
index 1996eb0..56c77de 100644
--- a/subprojects/core/src/main/groovy/org/gradle/initialization/DefaultProjectSpec.java
+++ b/subprojects/core/src/main/groovy/org/gradle/initialization/DefaultProjectSpec.java
@@ -15,7 +15,7 @@
  */
 package org.gradle.initialization;
 
-import org.gradle.api.internal.project.IProjectRegistry;
+import org.gradle.api.internal.project.ProjectRegistry;
 
 import java.io.File;
 
@@ -25,7 +25,7 @@ public class DefaultProjectSpec extends ProjectDirectoryProjectSpec {
     }
 
     @Override
-    protected void checkPreconditions(IProjectRegistry<?> registry) {
+    protected void checkPreconditions(ProjectRegistry<?> registry) {
         // Ignore
     }
 }
diff --git a/subprojects/core/src/main/groovy/org/gradle/initialization/DefaultSettings.groovy b/subprojects/core/src/main/groovy/org/gradle/initialization/DefaultSettings.groovy
index b5aedfe..64d2da4 100644
--- a/subprojects/core/src/main/groovy/org/gradle/initialization/DefaultSettings.groovy
+++ b/subprojects/core/src/main/groovy/org/gradle/initialization/DefaultSettings.groovy
@@ -18,19 +18,16 @@ package org.gradle.initialization
 
 import org.gradle.StartParameter
 import org.gradle.api.internal.GradleInternal
+import org.gradle.internal.service.scopes.ServiceRegistryFactory
 import org.gradle.groovy.scripts.ScriptSource
 
-/**
- * @author Hans Dockter
- */
 public class DefaultSettings extends BaseSettings {
-    public DefaultSettings() {}
 
-    DefaultSettings(GradleInternal gradle,
-                    IProjectDescriptorRegistry projectDescriptorRegistry,
-                    URLClassLoader classloader, File settingsDir,
+    DefaultSettings(ServiceRegistryFactory serviceRegistryFactory,
+                    GradleInternal gradle,
+                    ClassLoader classloader, File settingsDir,
                     ScriptSource settingsScript, StartParameter startParameter) {
-      super(gradle, projectDescriptorRegistry, classloader, settingsDir, settingsScript, startParameter)
+      super(serviceRegistryFactory, gradle, classloader, settingsDir, settingsScript, startParameter)
     }
 
 }
diff --git a/subprojects/core/src/main/groovy/org/gradle/initialization/DefaultSettingsFinder.java b/subprojects/core/src/main/groovy/org/gradle/initialization/DefaultSettingsFinder.java
index 6889452..c0c26c0 100644
--- a/subprojects/core/src/main/groovy/org/gradle/initialization/DefaultSettingsFinder.java
+++ b/subprojects/core/src/main/groovy/org/gradle/initialization/DefaultSettingsFinder.java
@@ -20,9 +20,6 @@ import org.gradle.initialization.layout.BuildLayout;
 import org.gradle.initialization.layout.BuildLayoutConfiguration;
 import org.gradle.initialization.layout.BuildLayoutFactory;
 
-/**
- * @author Hans Dockter
- */
 public class DefaultSettingsFinder implements ISettingsFinder {
     private final BuildLayoutFactory layoutFactory;
 
diff --git a/subprojects/core/src/main/groovy/org/gradle/initialization/DependencyResolutionLogger.java b/subprojects/core/src/main/groovy/org/gradle/initialization/DependencyResolutionLogger.java
index 7987192..a5e6801 100644
--- a/subprojects/core/src/main/groovy/org/gradle/initialization/DependencyResolutionLogger.java
+++ b/subprojects/core/src/main/groovy/org/gradle/initialization/DependencyResolutionLogger.java
@@ -22,7 +22,6 @@ import org.gradle.logging.ProgressLoggerFactory;
 
 import java.util.LinkedList;
 
-// TODO:DAZ Think about a better way to do thread-safety here, maybe
 public class DependencyResolutionLogger implements DependencyResolutionListener {
     private final ThreadLocal<LinkedList<ProgressLogger>> progressLoggers = new ThreadLocal<LinkedList<ProgressLogger>>() {
         protected LinkedList<ProgressLogger> initialValue() {
diff --git a/subprojects/core/src/main/groovy/org/gradle/initialization/GradleLauncherAction.java b/subprojects/core/src/main/groovy/org/gradle/initialization/GradleLauncherAction.java
deleted file mode 100644
index 2437d93..0000000
--- a/subprojects/core/src/main/groovy/org/gradle/initialization/GradleLauncherAction.java
+++ /dev/null
@@ -1,43 +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.initialization;
-
-import org.gradle.BuildResult;
-import org.gradle.GradleLauncher;
-
-/**
- * An object that performs some action with a {@link GradleLauncher}, and optionally produces a “result” object (e.g. the output).
- * <p>
- * Implementations of this are typically composed to bootstrap a build in a certain environment.
- * <p>
- * @see org.gradle.launcher.cli.ExecuteBuildAction
- * @see org.gradle.tooling.internal.provider.BuildModelAction
- */
-public interface GradleLauncherAction<T> {
-    
-    /**
-     * Something produced by the action, the meaning of which is entirely up to the implementation to define.
-     */
-    T getResult();
-
-    /**
-     * Executes the action with the given launcher.
-     * <p>
-     * The state of the launcher is not defined as part of this contract, it is highly context specific. For example,
-     * it is not guaranteed that the start parameter for the launcher has been configured.
-     */
-    BuildResult run(GradleLauncher launcher);
-}
diff --git a/subprojects/core/src/main/groovy/org/gradle/initialization/GradleLauncherFactory.java b/subprojects/core/src/main/groovy/org/gradle/initialization/GradleLauncherFactory.java
index a271e0f..d749341 100644
--- a/subprojects/core/src/main/groovy/org/gradle/initialization/GradleLauncherFactory.java
+++ b/subprojects/core/src/main/groovy/org/gradle/initialization/GradleLauncherFactory.java
@@ -21,8 +21,6 @@ import org.gradle.StartParameter;
 /**
  * <p>A {@code GradleLauncherFactory} is responsible for creating a {@link org.gradle.GradleLauncher} instance for a build, from a {@link
  * org.gradle.StartParameter}.</p>
- *
- * @author Hans Dockter
  */
 public interface GradleLauncherFactory {
     /**
diff --git a/subprojects/core/src/main/groovy/org/gradle/initialization/IGradlePropertiesLoader.java b/subprojects/core/src/main/groovy/org/gradle/initialization/IGradlePropertiesLoader.java
index 27db653..730e5c6 100644
--- a/subprojects/core/src/main/groovy/org/gradle/initialization/IGradlePropertiesLoader.java
+++ b/subprojects/core/src/main/groovy/org/gradle/initialization/IGradlePropertiesLoader.java
@@ -18,9 +18,6 @@ package org.gradle.initialization;
 import java.io.File;
 import java.util.Map;
 
-/**
- * @author Hans Dockter
- */
 public interface IGradlePropertiesLoader {
     public static final String SYSTEM_PROJECT_PROPERTIES_PREFIX = "org.gradle.project.";
 
diff --git a/subprojects/core/src/main/groovy/org/gradle/initialization/IProjectDescriptorRegistry.java b/subprojects/core/src/main/groovy/org/gradle/initialization/IProjectDescriptorRegistry.java
deleted file mode 100644
index 8dde639..0000000
--- a/subprojects/core/src/main/groovy/org/gradle/initialization/IProjectDescriptorRegistry.java
+++ /dev/null
@@ -1,26 +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.initialization;
-
-import org.gradle.api.internal.project.IProjectRegistry;
-import org.gradle.util.Path;
-
-/**
- * @author Hans Dockter
- */
-public interface IProjectDescriptorRegistry extends IProjectRegistry<DefaultProjectDescriptor> {
-    void changeDescriptorPath(Path oldPath, Path newPath);
-}
diff --git a/subprojects/core/src/main/groovy/org/gradle/initialization/ISettingsFinder.java b/subprojects/core/src/main/groovy/org/gradle/initialization/ISettingsFinder.java
index 62074f6..ee1c1c0 100644
--- a/subprojects/core/src/main/groovy/org/gradle/initialization/ISettingsFinder.java
+++ b/subprojects/core/src/main/groovy/org/gradle/initialization/ISettingsFinder.java
@@ -17,9 +17,6 @@ package org.gradle.initialization;
 
 import org.gradle.StartParameter;
 
-/**
- * @author Hans Dockter
- */
 public interface ISettingsFinder {
     SettingsLocation find(StartParameter startParameter);
 }
diff --git a/subprojects/core/src/main/groovy/org/gradle/initialization/InstantiatingBuildLoader.java b/subprojects/core/src/main/groovy/org/gradle/initialization/InstantiatingBuildLoader.java
index 3b13750..a2b5917 100644
--- a/subprojects/core/src/main/groovy/org/gradle/initialization/InstantiatingBuildLoader.java
+++ b/subprojects/core/src/main/groovy/org/gradle/initialization/InstantiatingBuildLoader.java
@@ -25,9 +25,6 @@ import org.gradle.api.internal.project.ProjectInternal;
 
 import java.io.File;
 
-/**
- * @author Hans Dockter
- */
 public class InstantiatingBuildLoader implements BuildLoader {
     private final IProjectFactory projectFactory;
 
diff --git a/subprojects/core/src/main/groovy/org/gradle/initialization/LayoutCommandLineConverter.java b/subprojects/core/src/main/groovy/org/gradle/initialization/LayoutCommandLineConverter.java
index db5ba26..b3f263a 100644
--- a/subprojects/core/src/main/groovy/org/gradle/initialization/LayoutCommandLineConverter.java
+++ b/subprojects/core/src/main/groovy/org/gradle/initialization/LayoutCommandLineConverter.java
@@ -23,13 +23,8 @@ import org.gradle.cli.AbstractCommandLineConverter;
 import org.gradle.cli.CommandLineArgumentException;
 import org.gradle.cli.CommandLineParser;
 import org.gradle.cli.ParsedCommandLine;
-import org.gradle.internal.SystemProperties;
 import org.gradle.internal.nativeplatform.filesystem.FileSystems;
-import org.gradle.util.GFileUtils;
 
-/**
- * by Szczepan Faber, created at: 2/18/13
- */
 public class LayoutCommandLineConverter extends AbstractCommandLineConverter<BuildLayoutParameters> {
 
     public static final String GRADLE_USER_HOME = "g";
@@ -37,7 +32,7 @@ public class LayoutCommandLineConverter extends AbstractCommandLineConverter<Bui
     private static final String PROJECT_DIR = "p";
 
     protected BuildLayoutParameters newInstance() {
-        return new BuildLayoutParameters().setProjectDir(GFileUtils.canonicalise(SystemProperties.getCurrentDir()));
+        return new BuildLayoutParameters();
     }
 
     public BuildLayoutParameters convert(ParsedCommandLine options, BuildLayoutParameters target) throws CommandLineArgumentException {
@@ -57,6 +52,6 @@ public class LayoutCommandLineConverter extends AbstractCommandLineConverter<Bui
     public void configure(CommandLineParser parser) {
         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(PROJECT_DIR, "project-dir").hasArgument().hasDescription("Specifies the start directory for Gradle. Defaults to current directory.");
-        parser.option(LayoutCommandLineConverter.GRADLE_USER_HOME, "gradle-user-home").hasArgument().hasDescription("Specifies the gradle user home directory.");
+        parser.option(GRADLE_USER_HOME, "gradle-user-home").hasArgument().hasDescription("Specifies the gradle user home directory.");
     }
 }
\ No newline at end of file
diff --git a/subprojects/core/src/main/groovy/org/gradle/initialization/ProjectAccessListener.java b/subprojects/core/src/main/groovy/org/gradle/initialization/ProjectAccessListener.java
index b4fa573..3a38789 100644
--- a/subprojects/core/src/main/groovy/org/gradle/initialization/ProjectAccessListener.java
+++ b/subprojects/core/src/main/groovy/org/gradle/initialization/ProjectAccessListener.java
@@ -20,8 +20,6 @@ import org.gradle.api.internal.project.ProjectInternal;
 
 /**
  * Internal interface, used by our configuration on demand mode.
- *
- * by Szczepan Faber, created at: 2/5/13
  */
 public interface ProjectAccessListener {
     void beforeRequestingTaskByPath(ProjectInternal targetProject);
diff --git a/subprojects/core/src/main/groovy/org/gradle/initialization/ProjectDescriptorRegistry.java b/subprojects/core/src/main/groovy/org/gradle/initialization/ProjectDescriptorRegistry.java
new file mode 100644
index 0000000..4df3a38
--- /dev/null
+++ b/subprojects/core/src/main/groovy/org/gradle/initialization/ProjectDescriptorRegistry.java
@@ -0,0 +1,23 @@
+/*
+ * 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.initialization;
+
+import org.gradle.api.internal.project.ProjectRegistry;
+import org.gradle.util.Path;
+
+public interface ProjectDescriptorRegistry extends ProjectRegistry<DefaultProjectDescriptor> {
+    void changeDescriptorPath(Path oldPath, Path newPath);
+}
diff --git a/subprojects/core/src/main/groovy/org/gradle/initialization/ProjectDirectoryProjectSpec.java b/subprojects/core/src/main/groovy/org/gradle/initialization/ProjectDirectoryProjectSpec.java
index 320603a..72991d6 100644
--- a/subprojects/core/src/main/groovy/org/gradle/initialization/ProjectDirectoryProjectSpec.java
+++ b/subprojects/core/src/main/groovy/org/gradle/initialization/ProjectDirectoryProjectSpec.java
@@ -18,7 +18,7 @@ package org.gradle.initialization;
 import org.apache.commons.lang.builder.EqualsBuilder;
 import org.apache.commons.lang.builder.HashCodeBuilder;
 import org.gradle.api.internal.project.ProjectIdentifier;
-import org.gradle.api.internal.project.IProjectRegistry;
+import org.gradle.api.internal.project.ProjectRegistry;
 import org.gradle.api.InvalidUserDataException;
 
 import java.io.File;
@@ -48,7 +48,7 @@ public class ProjectDirectoryProjectSpec extends AbstractProjectSpec implements
     }
 
     @Override
-    protected void checkPreconditions(IProjectRegistry<?> registry) {
+    protected void checkPreconditions(ProjectRegistry<?> registry) {
         if (!dir.exists()) {
             throw new InvalidUserDataException(String.format("Project directory '%s' does not exist.", dir));
         }
diff --git a/subprojects/core/src/main/groovy/org/gradle/initialization/ProjectPropertySettingBuildLoader.java b/subprojects/core/src/main/groovy/org/gradle/initialization/ProjectPropertySettingBuildLoader.java
index 6e6b30c..6e8fda7 100644
--- a/subprojects/core/src/main/groovy/org/gradle/initialization/ProjectPropertySettingBuildLoader.java
+++ b/subprojects/core/src/main/groovy/org/gradle/initialization/ProjectPropertySettingBuildLoader.java
@@ -30,9 +30,6 @@ import java.util.HashMap;
 import java.util.Map;
 import java.util.Properties;
 
-/**
- * @author Hans Dockter
- */
 public class ProjectPropertySettingBuildLoader implements BuildLoader {
     private static final Logger LOGGER = LoggerFactory.getLogger(ProjectPropertySettingBuildLoader.class);
 
diff --git a/subprojects/core/src/main/groovy/org/gradle/initialization/ProjectSpec.java b/subprojects/core/src/main/groovy/org/gradle/initialization/ProjectSpec.java
index b8a73a6..2b2133e 100644
--- a/subprojects/core/src/main/groovy/org/gradle/initialization/ProjectSpec.java
+++ b/subprojects/core/src/main/groovy/org/gradle/initialization/ProjectSpec.java
@@ -16,21 +16,21 @@
 package org.gradle.initialization;
 
 import org.gradle.api.internal.project.ProjectIdentifier;
-import org.gradle.api.internal.project.IProjectRegistry;
+import org.gradle.api.internal.project.ProjectRegistry;
 import org.gradle.api.InvalidUserDataException;
 
 public interface ProjectSpec {
     /**
      * Determines whether the given registry contains at least 1 project which meets this spec.
      */
-    boolean containsProject(IProjectRegistry<?> registry);
+    boolean containsProject(ProjectRegistry<?> registry);
 
     /**
      * Returns the single project in the given registry which meets this spec.
      * @return the project. Never returns null.
      * @throws InvalidUserDataException When project cannot be selected due to some user input mismatch.
      */
-    <T extends ProjectIdentifier> T selectProject(IProjectRegistry<? extends T> registry) throws
+    <T extends ProjectIdentifier> T selectProject(ProjectRegistry<? extends T> registry) throws
             InvalidUserDataException;
 
     /**
diff --git a/subprojects/core/src/main/groovy/org/gradle/initialization/PropertiesLoadingSettingsProcessor.java b/subprojects/core/src/main/groovy/org/gradle/initialization/PropertiesLoadingSettingsProcessor.java
index 2b3cb50..a86add0 100644
--- a/subprojects/core/src/main/groovy/org/gradle/initialization/PropertiesLoadingSettingsProcessor.java
+++ b/subprojects/core/src/main/groovy/org/gradle/initialization/PropertiesLoadingSettingsProcessor.java
@@ -15,11 +15,9 @@
  */
 package org.gradle.initialization;
 
+import org.gradle.StartParameter;
 import org.gradle.api.internal.GradleInternal;
 import org.gradle.api.internal.SettingsInternal;
-import org.gradle.StartParameter;
-
-import java.net.URLClassLoader;
 
 public class PropertiesLoadingSettingsProcessor implements SettingsProcessor {
     private final SettingsProcessor processor;
@@ -32,7 +30,7 @@ public class PropertiesLoadingSettingsProcessor implements SettingsProcessor {
 
     public SettingsInternal process(GradleInternal gradle,
                                     SettingsLocation settingsLocation,
-                                    URLClassLoader buildSourceClassLoader,
+                                    ClassLoader buildSourceClassLoader,
                                     StartParameter startParameter) {
         propertiesLoader.loadProperties(settingsLocation.getSettingsDir());
         return processor.process(gradle, settingsLocation, buildSourceClassLoader, startParameter);
diff --git a/subprojects/core/src/main/groovy/org/gradle/initialization/ScriptEvaluatingSettingsProcessor.java b/subprojects/core/src/main/groovy/org/gradle/initialization/ScriptEvaluatingSettingsProcessor.java
index 6656eb5..99e7c7f 100644
--- a/subprojects/core/src/main/groovy/org/gradle/initialization/ScriptEvaluatingSettingsProcessor.java
+++ b/subprojects/core/src/main/groovy/org/gradle/initialization/ScriptEvaluatingSettingsProcessor.java
@@ -25,14 +25,10 @@ import org.gradle.util.Clock;
 import org.slf4j.Logger;
 import org.slf4j.LoggerFactory;
 
-import java.net.URLClassLoader;
 import java.util.Collections;
 import java.util.Map;
 
 
-/**
- * @author Hans Dockter
- */
 public class ScriptEvaluatingSettingsProcessor implements SettingsProcessor {
     private static Logger logger = LoggerFactory.getLogger(ScriptEvaluatingSettingsProcessor.class);
 
@@ -50,20 +46,20 @@ public class ScriptEvaluatingSettingsProcessor implements SettingsProcessor {
 
     public SettingsInternal process(GradleInternal gradle,
                                     SettingsLocation settingsLocation,
-                                    URLClassLoader buildSourceClassLoader,
+                                    ClassLoader buildSourceClassLoader,
                                     StartParameter startParameter) {
         Clock settingsProcessingClock = new Clock();
         Map<String, String> properties = propertiesLoader.mergeProperties(Collections.<String, String>emptyMap());
         SettingsInternal settings = settingsFactory.createSettings(gradle, settingsLocation.getSettingsDir(),
                 settingsLocation.getSettingsScriptSource(), properties, startParameter, buildSourceClassLoader);
-        applySettingsScript(settingsLocation, buildSourceClassLoader, settings);
+        applySettingsScript(settingsLocation, settings);
         logger.debug("Timing: Processing settings took: {}", settingsProcessingClock.getTime());
         return settings;
     }
 
-    private void applySettingsScript(SettingsLocation settingsLocation, ClassLoader buildSourceClassLoader, SettingsInternal settings) {
+    private void applySettingsScript(SettingsLocation settingsLocation, SettingsInternal settings) {
         ScriptPlugin configurer = configurerFactory.create(settingsLocation.getSettingsScriptSource());
-        configurer.setClassLoader(buildSourceClassLoader);
+        configurer.setClassLoader(settings.getClassLoader());
         configurer.setScriptBaseClass(SettingsScript.class);
         configurer.apply(settings);
     }
diff --git a/subprojects/core/src/main/groovy/org/gradle/initialization/SettingsFactory.java b/subprojects/core/src/main/groovy/org/gradle/initialization/SettingsFactory.java
index ca633e8..16abfcc 100644
--- a/subprojects/core/src/main/groovy/org/gradle/initialization/SettingsFactory.java
+++ b/subprojects/core/src/main/groovy/org/gradle/initialization/SettingsFactory.java
@@ -18,31 +18,28 @@ package org.gradle.initialization;
 
 import org.gradle.StartParameter;
 import org.gradle.api.internal.*;
+import org.gradle.internal.service.scopes.ServiceRegistryFactory;
 import org.gradle.groovy.scripts.ScriptSource;
 import org.gradle.internal.reflect.Instantiator;
 
 import java.io.File;
-import java.net.URLClassLoader;
 import java.util.Map;
 
-/**
- * @author Hans Dockter
- */
 public class SettingsFactory {
-    private final IProjectDescriptorRegistry projectDescriptorRegistry;
     private final Instantiator instantiator;
+    private final ServiceRegistryFactory serviceRegistryFactory;
 
-    public SettingsFactory(IProjectDescriptorRegistry projectDescriptorRegistry, Instantiator instantiator) {
-        this.projectDescriptorRegistry = projectDescriptorRegistry;
+    public SettingsFactory(Instantiator instantiator, ServiceRegistryFactory serviceRegistryFactory) {
         this.instantiator = instantiator;
+        this.serviceRegistryFactory = serviceRegistryFactory;
     }
 
     public SettingsInternal createSettings(GradleInternal gradle, File settingsDir, ScriptSource settingsScript,
                                            Map<String, String> gradleProperties, StartParameter startParameter,
-                                           URLClassLoader classloader) {
+                                           ClassLoader classloader) {
 
-        DefaultSettings settings = instantiator.newInstance(DefaultSettings.class,
-                gradle, projectDescriptorRegistry, classloader, settingsDir, settingsScript, startParameter
+        DefaultSettings settings = instantiator.newInstance(DefaultSettings.class, serviceRegistryFactory,
+                gradle, classloader, settingsDir, settingsScript, startParameter
         );
 
         DynamicObject dynamicObject = ((DynamicObjectAware) settings).getAsDynamicObject();
diff --git a/subprojects/core/src/main/groovy/org/gradle/initialization/SettingsHandler.java b/subprojects/core/src/main/groovy/org/gradle/initialization/SettingsHandler.java
index fd88f4d..86fecda 100644
--- a/subprojects/core/src/main/groovy/org/gradle/initialization/SettingsHandler.java
+++ b/subprojects/core/src/main/groovy/org/gradle/initialization/SettingsHandler.java
@@ -20,9 +20,10 @@ import org.gradle.StartParameter;
 import org.gradle.api.initialization.ProjectDescriptor;
 import org.gradle.api.internal.GradleInternal;
 import org.gradle.api.internal.SettingsInternal;
+import org.gradle.initialization.buildsrc.BuildSourceBuilder;
+import org.gradle.invocation.BuildClassLoaderRegistry;
 
 import java.io.File;
-import java.net.URLClassLoader;
 
 /**
  * Handles locating and processing setting.gradle files.  Also deals with the buildSrc module, since that modules is
@@ -69,7 +70,7 @@ public class SettingsHandler {
             }
         }
 
-        gradle.getScriptClassLoader().addParent(settings.getClassLoader());
+        gradle.getServices().get(BuildClassLoaderRegistry.class).addRootClassLoader(settings.getClassLoader());
         return settings;
     }
 
@@ -87,7 +88,7 @@ public class SettingsHandler {
         StartParameter buildSrcStartParameter = startParameter.newBuild();
         buildSrcStartParameter.setCurrentDir(new File(settingsLocation.getSettingsDir(),
                 BaseSettings.DEFAULT_BUILD_SRC_DIR));
-        URLClassLoader buildSourceClassLoader = buildSourceBuilder.buildAndCreateClassLoader(buildSrcStartParameter);
+        ClassLoader buildSourceClassLoader = buildSourceBuilder.buildAndCreateClassLoader(buildSrcStartParameter);
 
         return loadSettings(gradle, settingsLocation, buildSourceClassLoader, startParameter);
     }
@@ -97,7 +98,7 @@ public class SettingsHandler {
     }
 
     private SettingsInternal loadSettings(GradleInternal gradle, SettingsLocation settingsLocation,
-                                          URLClassLoader buildSourceClassLoader, StartParameter startParameter) {
+                                          ClassLoader buildSourceClassLoader, StartParameter startParameter) {
         return settingsProcessor.process(gradle, settingsLocation, buildSourceClassLoader, startParameter);
     }
 }
diff --git a/subprojects/core/src/main/groovy/org/gradle/initialization/SettingsProcessor.java b/subprojects/core/src/main/groovy/org/gradle/initialization/SettingsProcessor.java
index 7ea4f02..1ca1e93 100644
--- a/subprojects/core/src/main/groovy/org/gradle/initialization/SettingsProcessor.java
+++ b/subprojects/core/src/main/groovy/org/gradle/initialization/SettingsProcessor.java
@@ -15,11 +15,9 @@
  */
 package org.gradle.initialization;
 
+import org.gradle.StartParameter;
 import org.gradle.api.internal.GradleInternal;
 import org.gradle.api.internal.SettingsInternal;
-import org.gradle.StartParameter;
-
-import java.net.URLClassLoader;
 
 /**
  * Responsible for locating, constructing, and evaluating the {@link SettingsInternal} for a build.
@@ -27,6 +25,6 @@ import java.net.URLClassLoader;
 public interface SettingsProcessor {
     SettingsInternal process(GradleInternal gradle,
                              SettingsLocation settingsLocation,
-                             URLClassLoader buildSourceClassLoader,
+                             ClassLoader buildSourceClassLoader,
                              StartParameter startParameter);
 }
diff --git a/subprojects/core/src/main/groovy/org/gradle/initialization/buildsrc/BuildSourceBuilder.java b/subprojects/core/src/main/groovy/org/gradle/initialization/buildsrc/BuildSourceBuilder.java
new file mode 100644
index 0000000..0ebff93
--- /dev/null
+++ b/subprojects/core/src/main/groovy/org/gradle/initialization/buildsrc/BuildSourceBuilder.java
@@ -0,0 +1,86 @@
+/*
+ * Copyright 2013 the original author or authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS 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.buildsrc;
+
+import org.gradle.GradleLauncher;
+import org.gradle.StartParameter;
+import org.gradle.cache.CacheBuilder;
+import org.gradle.cache.CacheRepository;
+import org.gradle.cache.PersistentCache;
+import org.gradle.cache.internal.FileLockManager;
+import org.gradle.initialization.ClassLoaderRegistry;
+import org.gradle.initialization.GradleLauncherFactory;
+import org.gradle.internal.classpath.ClassPath;
+import org.gradle.internal.classpath.DefaultClassPath;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+import java.net.URLClassLoader;
+
+public class BuildSourceBuilder {
+    private static final Logger LOGGER = LoggerFactory.getLogger(BuildSourceBuilder.class);
+
+    private final GradleLauncherFactory gradleLauncherFactory;
+    private final ClassLoaderRegistry classLoaderRegistry;
+    private final CacheRepository cacheRepository;
+
+    public BuildSourceBuilder(GradleLauncherFactory gradleLauncherFactory, ClassLoaderRegistry classLoaderRegistry, CacheRepository cacheRepository) {
+        this.gradleLauncherFactory = gradleLauncherFactory;
+        this.classLoaderRegistry = classLoaderRegistry;
+        this.cacheRepository = cacheRepository;
+    }
+
+    public ClassLoader buildAndCreateClassLoader(StartParameter startParameter) {
+        ClassPath classpath = createBuildSourceClasspath(startParameter);
+        return new URLClassLoader(classpath.getAsURLArray(), classLoaderRegistry.getGradleApiClassLoader());
+    }
+
+    ClassPath createBuildSourceClasspath(StartParameter startParameter) {
+        assert startParameter.getCurrentDir() != null && startParameter.getBuildFile() == null;
+
+        LOGGER.debug("Starting to build the build sources.");
+        if (!startParameter.getCurrentDir().isDirectory()) {
+            LOGGER.debug("Gradle source dir does not exist. We leave.");
+            return new DefaultClassPath();
+        }
+        LOGGER.info("================================================" + " Start building buildSrc");
+
+        // If we were not the most recent version of Gradle to build the buildSrc dir, then do a clean build
+        // Otherwise, just to a regular build
+        final PersistentCache buildSrcCache = createCache(startParameter);
+
+        GradleLauncher gradleLauncher = buildGradleLauncher(startParameter);
+        return buildSrcCache.useCache("rebuild buildSrc", new BuildSrcUpdateFactory(buildSrcCache, gradleLauncher, new BuildSrcBuildListenerFactory()));
+    }
+
+    PersistentCache createCache(StartParameter startParameter) {
+        return cacheRepository.
+                    cache("buildSrc").
+                    withLockMode(FileLockManager.LockMode.None).
+                    forObject(startParameter.getCurrentDir()).
+                    withVersionStrategy(CacheBuilder.VersionStrategy.SharedCacheInvalidateOnVersionChange).
+                    open();
+    }
+
+    private GradleLauncher buildGradleLauncher(StartParameter startParameter) {
+        final StartParameter startParameterArg = startParameter.newInstance();
+        startParameterArg.setProjectProperties(startParameter.getProjectProperties());
+        startParameterArg.setSearchUpwards(false);
+        startParameterArg.setProfile(startParameter.isProfile());
+        return gradleLauncherFactory.newInstance(startParameterArg);
+    }
+}
diff --git a/subprojects/core/src/main/groovy/org/gradle/initialization/buildsrc/BuildSrcBuildListenerFactory.java b/subprojects/core/src/main/groovy/org/gradle/initialization/buildsrc/BuildSrcBuildListenerFactory.java
new file mode 100644
index 0000000..e52586d
--- /dev/null
+++ b/subprojects/core/src/main/groovy/org/gradle/initialization/buildsrc/BuildSrcBuildListenerFactory.java
@@ -0,0 +1,61 @@
+/*
+ * Copyright 2013 the original author or authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS 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.buildsrc;
+
+import org.gradle.BuildAdapter;
+import org.gradle.api.internal.GradleInternal;
+import org.gradle.api.internal.plugins.EmbeddableJavaProject;
+import org.gradle.api.invocation.Gradle;
+import org.gradle.initialization.ModelConfigurationListener;
+import org.gradle.util.WrapUtil;
+
+import java.io.File;
+import java.util.Collection;
+import java.util.Set;
+
+public class BuildSrcBuildListenerFactory {
+
+    private static final String DEFAULT_BUILD_SOURCE_SCRIPT_RESOURCE = "defaultBuildSourceScript.txt";
+
+    Listener create(boolean rebuild) {
+        return new Listener(rebuild);
+    }
+
+    public static class Listener extends BuildAdapter implements ModelConfigurationListener {
+        private Set<File> classpath;
+        private final boolean rebuild;
+
+        public Listener(boolean rebuild) {
+            this.rebuild = rebuild;
+        }
+
+        @Override
+        public void projectsLoaded(Gradle gradle) {
+            gradle.getRootProject().apply(WrapUtil.toMap("from", BuildSrcBuildListenerFactory.class.getResource(DEFAULT_BUILD_SOURCE_SCRIPT_RESOURCE)));
+        }
+
+        public Collection<File> getRuntimeClasspath() {
+            return classpath;
+        }
+
+        public void onConfigure(GradleInternal gradle) {
+            EmbeddableJavaProject projectInfo = gradle.getRootProject().getConvention().getPlugin(EmbeddableJavaProject.class);
+            gradle.getStartParameter().setTaskNames(rebuild ? projectInfo.getRebuildTasks() : projectInfo.getBuildTasks());
+            classpath = projectInfo.getRuntimeClasspath().getFiles();
+        }
+    }
+}
diff --git a/subprojects/core/src/main/groovy/org/gradle/initialization/buildsrc/BuildSrcUpdateFactory.java b/subprojects/core/src/main/groovy/org/gradle/initialization/buildsrc/BuildSrcUpdateFactory.java
new file mode 100644
index 0000000..31fe50b
--- /dev/null
+++ b/subprojects/core/src/main/groovy/org/gradle/initialization/buildsrc/BuildSrcUpdateFactory.java
@@ -0,0 +1,61 @@
+/*
+ * Copyright 2013 the original author or authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS 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.buildsrc;
+
+import org.gradle.GradleLauncher;
+import org.gradle.api.UncheckedIOException;
+import org.gradle.api.logging.Logger;
+import org.gradle.api.logging.Logging;
+import org.gradle.cache.PersistentCache;
+import org.gradle.internal.Factory;
+import org.gradle.internal.classpath.DefaultClassPath;
+
+import java.io.File;
+import java.io.IOException;
+import java.util.Collection;
+
+public class BuildSrcUpdateFactory implements Factory<DefaultClassPath> {
+    private final PersistentCache cache;
+    private final GradleLauncher gradleLauncher;
+    private BuildSrcBuildListenerFactory listenerFactory;
+    private static final Logger LOGGER = Logging.getLogger(BuildSrcUpdateFactory.class);
+
+    public BuildSrcUpdateFactory(PersistentCache cache, GradleLauncher gradleLauncher, BuildSrcBuildListenerFactory listenerFactory) {
+        this.cache = cache;
+        this.gradleLauncher = gradleLauncher;
+        this.listenerFactory = listenerFactory;
+    }
+
+    public DefaultClassPath create() {
+        File markerFile = new File(cache.getBaseDir(), "built.bin");
+        final boolean rebuild = !markerFile.exists();
+
+        BuildSrcBuildListenerFactory.Listener listener = listenerFactory.create(rebuild);
+        gradleLauncher.addListener(listener);
+        gradleLauncher.run().rethrowFailure();
+
+        Collection<File> classpath = listener.getRuntimeClasspath();
+        LOGGER.debug("Gradle source classpath is: {}", classpath);
+        LOGGER.info("================================================" + " Finished building buildSrc");
+        try {
+            markerFile.createNewFile();
+        } catch (IOException e) {
+            throw new UncheckedIOException(e);
+        }
+        return new DefaultClassPath(classpath);
+    }
+}
diff --git a/subprojects/core/src/main/groovy/org/gradle/internal/featurelifecycle/DeprecatedFeatureHandler.java b/subprojects/core/src/main/groovy/org/gradle/internal/featurelifecycle/DeprecatedFeatureHandler.java
new file mode 100644
index 0000000..16f3cad
--- /dev/null
+++ b/subprojects/core/src/main/groovy/org/gradle/internal/featurelifecycle/DeprecatedFeatureHandler.java
@@ -0,0 +1,26 @@
+/*
+ * Copyright 2013 the original author or authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.gradle.internal.featurelifecycle;
+
+/**
+ * Notified when a deprecated feature is used.
+ *
+ * <p>Implementations need not be thread-safe.
+ */
+public interface DeprecatedFeatureHandler {
+    void deprecatedFeatureUsed(DeprecatedFeatureUsage usage);
+}
diff --git a/subprojects/core/src/main/groovy/org/gradle/internal/featurelifecycle/DeprecatedFeatureUsage.java b/subprojects/core/src/main/groovy/org/gradle/internal/featurelifecycle/DeprecatedFeatureUsage.java
new file mode 100644
index 0000000..5971aa2
--- /dev/null
+++ b/subprojects/core/src/main/groovy/org/gradle/internal/featurelifecycle/DeprecatedFeatureUsage.java
@@ -0,0 +1,77 @@
+/*
+ * Copyright 2013 the original author or authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.gradle.internal.featurelifecycle;
+
+import org.codehaus.groovy.runtime.StackTraceUtils;
+
+import java.util.ArrayList;
+import java.util.Collections;
+import java.util.List;
+
+/**
+ * An immutable description of the usage of a deprecated feature.
+ */
+public class DeprecatedFeatureUsage {
+    private final String message;
+    private final Class<?> calledFrom;
+    private final List<StackTraceElement> stack;
+
+    public DeprecatedFeatureUsage(String message, Class<?> calledFrom) {
+        this.message = message;
+        this.calledFrom = calledFrom;
+        this.stack = Collections.emptyList();
+    }
+
+    private DeprecatedFeatureUsage(DeprecatedFeatureUsage usage, List<StackTraceElement> stack) {
+        this.message = usage.message;
+        this.calledFrom = usage.calledFrom;
+        this.stack = stack;
+    }
+
+    public String getMessage() {
+        return message;
+    }
+
+    public List<StackTraceElement> getStack() {
+        return stack;
+    }
+
+    /**
+     * Creates a copy of this usage with the stack trace populated. Implementation is a bit limited in that it assumes that
+     * this method is called from the same thread that triggered the usage.
+     */
+    public DeprecatedFeatureUsage withStackTrace() {
+        if (!stack.isEmpty()) {
+            return this;
+        }
+
+        StackTraceElement[] originalStack = StackTraceUtils.sanitize(new Exception()).getStackTrace();
+        int caller = 0;
+        while (caller < originalStack.length && !originalStack[caller].getClassName().startsWith(calledFrom.getName())) {
+            caller++;
+        }
+        while (caller < originalStack.length && originalStack[caller].getClassName().startsWith(calledFrom.getName())) {
+            caller++;
+        }
+        caller++;
+        List<StackTraceElement> stack = new ArrayList<StackTraceElement>();
+        for (; caller < originalStack.length; caller++) {
+            stack.add(originalStack[caller]);
+        }
+        return new DeprecatedFeatureUsage(this, stack);
+    }
+}
diff --git a/subprojects/core/src/main/groovy/org/gradle/internal/featurelifecycle/LoggingDeprecatedFeatureHandler.java b/subprojects/core/src/main/groovy/org/gradle/internal/featurelifecycle/LoggingDeprecatedFeatureHandler.java
new file mode 100644
index 0000000..4b10d5d
--- /dev/null
+++ b/subprojects/core/src/main/groovy/org/gradle/internal/featurelifecycle/LoggingDeprecatedFeatureHandler.java
@@ -0,0 +1,76 @@
+/*
+ * Copyright 2013 the original author or authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.gradle.internal.featurelifecycle;
+
+import org.gradle.api.logging.Logger;
+import org.gradle.api.logging.Logging;
+import org.gradle.internal.SystemProperties;
+import org.gradle.util.SingleMessageLogger;
+
+import java.util.HashSet;
+import java.util.List;
+import java.util.Set;
+
+public class LoggingDeprecatedFeatureHandler implements DeprecatedFeatureHandler {
+    private static final Logger LOGGER = Logging.getLogger(LoggingDeprecatedFeatureHandler.class);
+    private final Set<String> messages = new HashSet<String>();
+    private UsageLocationReporter locationReporter;
+
+    public LoggingDeprecatedFeatureHandler() {
+        this(new UsageLocationReporter() {
+            public void reportLocation(DeprecatedFeatureUsage usage, StringBuilder target) {
+            }
+        });
+    }
+
+    public LoggingDeprecatedFeatureHandler(UsageLocationReporter locationReporter) {
+        this.locationReporter = locationReporter;
+    }
+
+    public void setLocationReporter(UsageLocationReporter locationReporter) {
+        this.locationReporter = locationReporter;
+    }
+
+    public void deprecatedFeatureUsed(DeprecatedFeatureUsage usage) {
+        if (messages.add(usage.getMessage())) {
+            usage = usage.withStackTrace();
+            StringBuilder message = new StringBuilder();
+            locationReporter.reportLocation(usage, message);
+            if (message.length() > 0) {
+                message.append(SystemProperties.getLineSeparator());
+            }
+            message.append(usage.getMessage());
+            logTraceIfNecessary(usage.getStack(), message);
+            LOGGER.warn(message.toString());
+        }
+    }
+
+    private void logTraceIfNecessary(List<StackTraceElement> stack, StringBuilder message) {
+        if (isTraceLoggingEnabled()) {
+            for (StackTraceElement frame : stack) {
+                message.append(SystemProperties.getLineSeparator());
+                message.append("    ");
+                message.append(frame.toString());
+            }
+        }
+    }
+
+    private static boolean isTraceLoggingEnabled() {
+        return Boolean.getBoolean(SingleMessageLogger.ORG_GRADLE_DEPRECATION_TRACE_PROPERTY_NAME);
+    }
+
+}
diff --git a/subprojects/core/src/main/groovy/org/gradle/internal/featurelifecycle/ScriptUsageLocationReporter.java b/subprojects/core/src/main/groovy/org/gradle/internal/featurelifecycle/ScriptUsageLocationReporter.java
new file mode 100644
index 0000000..8e1b375
--- /dev/null
+++ b/subprojects/core/src/main/groovy/org/gradle/internal/featurelifecycle/ScriptUsageLocationReporter.java
@@ -0,0 +1,91 @@
+/*
+ * Copyright 2013 the original author or authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.gradle.internal.featurelifecycle;
+
+import net.jcip.annotations.ThreadSafe;
+import org.apache.commons.lang.StringUtils;
+import org.gradle.groovy.scripts.Script;
+import org.gradle.groovy.scripts.ScriptExecutionListener;
+import org.gradle.groovy.scripts.ScriptSource;
+
+import java.util.HashMap;
+import java.util.List;
+import java.util.Map;
+import java.util.concurrent.locks.Lock;
+import java.util.concurrent.locks.ReentrantLock;
+
+ at ThreadSafe
+public class ScriptUsageLocationReporter implements ScriptExecutionListener, UsageLocationReporter {
+    private final Lock lock = new ReentrantLock();
+    private final Map<String, ScriptSource> scripts = new HashMap<String, ScriptSource>();
+
+    public void beforeScript(Script script) {
+        lock.lock();
+        try {
+            ScriptSource scriptSource = script.getScriptSource();
+            scripts.put(scriptSource.getFileName(), scriptSource);
+        } finally {
+            lock.unlock();
+        }
+    }
+
+    public void afterScript(Script script, Throwable result) {
+    }
+
+    public void reportLocation(DeprecatedFeatureUsage usage, StringBuilder target) {
+        lock.lock();
+        try {
+            doReportLocation(usage, target);
+        } finally {
+            lock.unlock();
+        }
+    }
+
+    private void doReportLocation(DeprecatedFeatureUsage usage, StringBuilder target) {
+        List<StackTraceElement> stack = usage.getStack();
+        if (stack.isEmpty()) {
+            return;
+        }
+
+        StackTraceElement directCaller = stack.get(0);
+        if (scripts.containsKey(directCaller.getFileName())) {
+            reportStackTraceElement(directCaller, target);
+            return;
+        }
+
+        int caller = 1;
+        while (caller < stack.size() && stack.get(caller).getClassName().equals(directCaller.getClassName())) {
+            caller++;
+        }
+        if (caller == stack.size()) {
+            return;
+        }
+        StackTraceElement indirectCaller = stack.get(caller);
+        if (scripts.containsKey(indirectCaller.getFileName())) {
+            reportStackTraceElement(indirectCaller, target);
+        }
+    }
+
+    private void reportStackTraceElement(StackTraceElement stackTraceElement, StringBuilder target) {
+        ScriptSource scriptSource = scripts.get(stackTraceElement.getFileName());
+        target.append(StringUtils.capitalize(scriptSource.getDisplayName()));
+        if (stackTraceElement.getLineNumber() > 0) {
+            target.append(": line ");
+            target.append(stackTraceElement.getLineNumber());
+        }
+    }
+}
diff --git a/subprojects/core/src/main/groovy/org/gradle/internal/featurelifecycle/UsageLocationReporter.java b/subprojects/core/src/main/groovy/org/gradle/internal/featurelifecycle/UsageLocationReporter.java
new file mode 100644
index 0000000..2891692
--- /dev/null
+++ b/subprojects/core/src/main/groovy/org/gradle/internal/featurelifecycle/UsageLocationReporter.java
@@ -0,0 +1,21 @@
+/*
+ * Copyright 2013 the original author or authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.gradle.internal.featurelifecycle;
+
+public interface UsageLocationReporter {
+    void reportLocation(DeprecatedFeatureUsage usage, StringBuilder target);
+}
diff --git a/subprojects/core/src/main/groovy/org/gradle/internal/graph/CachingDirectedGraphWalker.java b/subprojects/core/src/main/groovy/org/gradle/internal/graph/CachingDirectedGraphWalker.java
new file mode 100644
index 0000000..a2eeb73
--- /dev/null
+++ b/subprojects/core/src/main/groovy/org/gradle/internal/graph/CachingDirectedGraphWalker.java
@@ -0,0 +1,203 @@
+/*
+ * Copyright 2010 the original author or authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.gradle.internal.graph;
+
+import org.gradle.util.GUtil;
+
+import java.util.*;
+
+/**
+ * A graph walker which collects the values reachable from a given set of start nodes. Handles cycles in the graph. Can
+ * be reused to perform multiple searches, and reuses the results of previous searches.
+ *
+ * Uses a variation of Tarjan's algorithm: http://en.wikipedia.org/wiki/Tarjan%27s_strongly_connected_components_algorithm
+ */
+public class CachingDirectedGraphWalker<N, T> {
+    private final DirectedGraphWithEdgeValues<N, T> graph;
+    private List<N> startNodes = new LinkedList<N>();
+    private Set<NodeDetails<N, T>> strongComponents = new LinkedHashSet<NodeDetails<N, T>>();
+    private final Map<N, Set<T>> cachedNodeValues = new HashMap<N, Set<T>>();
+
+    public CachingDirectedGraphWalker(DirectedGraph<N, T> graph) {
+        this.graph = new GraphWithEmpyEdges<N, T>(graph);
+    }
+
+    public CachingDirectedGraphWalker(DirectedGraphWithEdgeValues<N, T> graph) {
+        this.graph = graph;
+    }
+
+    /**
+     * Adds some start nodes.
+     */
+    public CachingDirectedGraphWalker<N, T> add(N... values) {
+        add(Arrays.asList(values));
+        return this;
+    }
+
+    /**
+     * Adds some start nodes.
+     */
+    public CachingDirectedGraphWalker add(Iterable<? extends N> values) {
+        GUtil.addToCollection(startNodes, values);
+        return this;
+    }
+
+    /**
+     * Calculates the set of values of nodes reachable from the start nodes.
+     */
+    public Set<T> findValues() {
+        try {
+            return doSearch();
+        } finally {
+            startNodes.clear();
+        }
+    }
+
+    /**
+     * Returns the set of cycles seen in the graph.
+     */
+    public List<Set<N>> findCycles() {
+        findValues();
+        List<Set<N>> result = new ArrayList<Set<N>>();
+        for (NodeDetails<N, T> nodeDetails : strongComponents) {
+            Set<N> componentMembers = new LinkedHashSet<N>();
+            for (NodeDetails<N, T> componentMember : nodeDetails.componentMembers) {
+                componentMembers.add(componentMember.node);
+            }
+            result.add(componentMembers);
+        }
+        return result;
+    }
+
+    private Set<T> doSearch() {
+        int componentCount = 0;
+        Map<N, NodeDetails<N, T>> seenNodes = new HashMap<N, NodeDetails<N, T>>();
+        Map<Integer, NodeDetails<N, T>> components = new HashMap<Integer, NodeDetails<N, T>>();
+        LinkedList<N> queue = new LinkedList<N>(startNodes);
+
+        while (!queue.isEmpty()) {
+            N node = queue.getFirst();
+            NodeDetails<N, T> details = seenNodes.get(node);
+            if (details == null) {
+                // Have not visited this node yet. Push its successors onto the queue in front of this node and visit
+                // them
+
+                details = new NodeDetails<N, T>(node, componentCount++);
+                seenNodes.put(node, details);
+                components.put(details.component, details);
+
+                Set<T> cacheValues = cachedNodeValues.get(node);
+                if (cacheValues != null) {
+                    // Already visited this node
+                    details.values = cacheValues;
+                    details.finished = true;
+                    queue.removeFirst();
+                    continue;
+                }
+
+                graph.getNodeValues(node, details.values, details.successors);
+                for (N connectedNode : details.successors) {
+                    NodeDetails<N, T> connectedNodeDetails = seenNodes.get(connectedNode);
+                    if (connectedNodeDetails == null) {
+                        // Have not visited the successor node, so add to the queue for visiting
+                        queue.add(0, connectedNode);
+                    } else if (!connectedNodeDetails.finished) {
+                        // Currently visiting the successor node - we're in a cycle
+                        details.stronglyConnected = true;
+                    }
+                    // Else, already visited
+                }
+            } else {
+                // Have visited all of this node's successors
+                queue.removeFirst();
+
+                if (cachedNodeValues.containsKey(node)) {
+                    continue;
+                }
+
+                for (N connectedNode : details.successors) {
+                    NodeDetails<N, T> connectedNodeDetails = seenNodes.get(connectedNode);
+                    if (!connectedNodeDetails.finished) {
+                        // part of a cycle
+                        details.minSeen = Math.min(details.minSeen, connectedNodeDetails.minSeen);
+                        details.stronglyConnected = true;
+                    }
+                    details.values.addAll(connectedNodeDetails.values);
+                    graph.getEdgeValues(node, connectedNode, details.values);
+                }
+
+                if (details.minSeen != details.component) {
+                    // Part of a strongly connected component (ie cycle) - move values to root of the component
+                    // The root is the first node of the component we encountered
+                    NodeDetails<N, T> rootDetails = components.get(details.minSeen);
+                    rootDetails.values.addAll(details.values);
+                    details.values.clear();
+                    rootDetails.componentMembers.addAll(details.componentMembers);
+                } else {
+                    // Not part of a strongly connected component or the root of a strongly connected component
+                    for (NodeDetails<N, T> componentMember : details.componentMembers) {
+                        cachedNodeValues.put(componentMember.node, details.values);
+                        componentMember.finished = true;
+                        components.remove(componentMember.component);
+                    }
+                    if (details.stronglyConnected) {
+                        strongComponents.add(details);
+                    }
+                }
+            }
+        }
+
+        Set<T> values = new LinkedHashSet<T>();
+        for (N startNode : startNodes) {
+            values.addAll(cachedNodeValues.get(startNode));
+        }
+        return values;
+    }
+
+    private static class NodeDetails<N, T> {
+        private final int component;
+        private final N node;
+        private Set<T> values = new LinkedHashSet<T>();
+        private List<N> successors = new ArrayList<N>();
+        private Set<NodeDetails<N, T>> componentMembers = new LinkedHashSet<NodeDetails<N, T>>();
+        private int minSeen;
+        private boolean stronglyConnected;
+        private boolean finished;
+
+        public NodeDetails(N node, int component) {
+            this.node = node;
+            this.component = component;
+            minSeen = component;
+            componentMembers.add(this);
+        }
+    }
+
+    private static class GraphWithEmpyEdges<N, T> implements DirectedGraphWithEdgeValues<N, T> {
+        private final DirectedGraph<N, T> graph;
+
+        public GraphWithEmpyEdges(DirectedGraph<N, T> graph) {
+            this.graph = graph;
+        }
+
+        public void getEdgeValues(N from, N to, Collection<T> values) {
+        }
+
+        public void getNodeValues(N node, Collection<? super T> values, Collection<? super N> connectedNodes) {
+            graph.getNodeValues(node, values, connectedNodes);
+        }
+    }
+}
diff --git a/subprojects/core/src/main/groovy/org/gradle/internal/graph/DirectedGraph.java b/subprojects/core/src/main/groovy/org/gradle/internal/graph/DirectedGraph.java
new file mode 100644
index 0000000..a1d8483
--- /dev/null
+++ b/subprojects/core/src/main/groovy/org/gradle/internal/graph/DirectedGraph.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.internal.graph;
+
+import java.util.Collection;
+
+/**
+ * A directed graph with nodes of type N. Each node has a collection of values of type V.
+ */
+public interface DirectedGraph<N, V> {
+    void getNodeValues(N node, Collection<? super V> values, Collection<? super N> connectedNodes);
+}
diff --git a/subprojects/core/src/main/groovy/org/gradle/internal/graph/DirectedGraphRenderer.java b/subprojects/core/src/main/groovy/org/gradle/internal/graph/DirectedGraphRenderer.java
new file mode 100644
index 0000000..9376900
--- /dev/null
+++ b/subprojects/core/src/main/groovy/org/gradle/internal/graph/DirectedGraphRenderer.java
@@ -0,0 +1,80 @@
+/*
+ * Copyright 2013 the original author or authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.gradle.internal.graph;
+
+import org.gradle.api.Action;
+import org.gradle.logging.StyledTextOutput;
+import org.gradle.logging.internal.StreamingStyledTextOutput;
+
+import java.util.*;
+
+import static org.gradle.logging.StyledTextOutput.Style.Info;
+
+public class DirectedGraphRenderer<N> {
+    private final GraphNodeRenderer<N> nodeRenderer;
+    private final DirectedGraph<N, ?> graph;
+    private boolean omittedDetails;
+
+    public DirectedGraphRenderer(GraphNodeRenderer<N> nodeRenderer, DirectedGraph<N, ?> graph) {
+        this.nodeRenderer = nodeRenderer;
+        this.graph = graph;
+    }
+
+    public void renderTo(N root, Appendable output) {
+        renderTo(root, new StreamingStyledTextOutput(output));
+    }
+
+    public void renderTo(N root, StyledTextOutput output) {
+        GraphRenderer renderer = new GraphRenderer(output);
+        Set<N> rendered = new HashSet<N>();
+        omittedDetails = false;
+        renderTo(root, renderer, rendered, false);
+        if (omittedDetails) {
+            output.println();
+            output.withStyle(Info).println("(*) - details omitted (listed previously)");
+        }
+    }
+
+    private void renderTo(final N node, GraphRenderer graphRenderer, Collection<N> rendered, boolean lastChild) {
+        final boolean alreadySeen = !rendered.add(node);
+
+        graphRenderer.visit(new Action<StyledTextOutput>() {
+            public void execute(StyledTextOutput output) {
+                nodeRenderer.renderTo(node, output);
+                if (alreadySeen) {
+                    output.text(" (*)");
+                }
+            }
+        }, lastChild);
+
+        if (alreadySeen) {
+            omittedDetails = true;
+            return;
+        }
+
+        List<N> children = new ArrayList<N>();
+        graph.getNodeValues(node, new HashSet<Object>(), children);
+        if (children.isEmpty()) {
+            return;
+        }
+        graphRenderer.startChildren();
+        for (int i = 0; i < children.size(); i++) {
+            N child = children.get(i);
+            renderTo(child, graphRenderer, rendered, i == children.size() - 1);
+        }
+        graphRenderer.completeChildren();
+    }
+}
diff --git a/subprojects/core/src/main/groovy/org/gradle/internal/graph/DirectedGraphWithEdgeValues.java b/subprojects/core/src/main/groovy/org/gradle/internal/graph/DirectedGraphWithEdgeValues.java
new file mode 100644
index 0000000..5dcf7cb
--- /dev/null
+++ b/subprojects/core/src/main/groovy/org/gradle/internal/graph/DirectedGraphWithEdgeValues.java
@@ -0,0 +1,25 @@
+/*
+ * Copyright 2010 the original author or authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.gradle.internal.graph;
+
+import java.util.Collection;
+
+/**
+ * A directed graph with nodes of type N. Each edge has a collection of values of type V
+ */
+public interface DirectedGraphWithEdgeValues<N, V> extends DirectedGraph<N, V> {
+    void getEdgeValues(N from, N to, Collection<V> values);
+}
diff --git a/subprojects/core/src/main/groovy/org/gradle/internal/graph/GraphAggregator.java b/subprojects/core/src/main/groovy/org/gradle/internal/graph/GraphAggregator.java
new file mode 100644
index 0000000..ae97929
--- /dev/null
+++ b/subprojects/core/src/main/groovy/org/gradle/internal/graph/GraphAggregator.java
@@ -0,0 +1,90 @@
+/*
+ * Copyright 2010 the original author or authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.gradle.internal.graph;
+
+import java.util.*;
+
+/**
+ * Groups the nodes of a graph based on their reachability from a set of starting nodes.
+ */
+public class GraphAggregator<N> {
+    private final CachingDirectedGraphWalker<N, N> graphWalker;
+
+    public GraphAggregator(DirectedGraph<N, ?> graph) {
+        graphWalker = new CachingDirectedGraphWalker<N, N>(new ConnectedNodesAsValuesDirectedGraph<N>(graph));
+    }
+
+    public Result<N> group(Collection<? extends N> startNodes, Collection<? extends N> allNodes) {
+        Map<N, Set<N>> reachableByNode = new HashMap<N, Set<N>>();
+        Set<N> topLevelNodes = new LinkedHashSet<N>(allNodes);
+        for (N node : allNodes) {
+            Set<N> reachableNodes = graphWalker.add(node).findValues();
+            reachableByNode.put(node, reachableNodes);
+            topLevelNodes.removeAll(reachableNodes);
+        }
+        topLevelNodes.addAll(startNodes);
+        Map<N, Set<N>> nodes = new HashMap<N, Set<N>>();
+        for (N node : topLevelNodes) {
+            nodes.put(node, calculateReachableNodes(reachableByNode, node, topLevelNodes));
+        }
+        return new Result<N>(nodes, topLevelNodes);
+    }
+
+    private Set<N> calculateReachableNodes(Map<N, Set<N>> nodes, N node, Set<N> topLevelNodes) {
+        Set<N> reachableNodes = nodes.get(node);
+        reachableNodes.add(node);
+        Set<N> reachableStartNodes = new LinkedHashSet<N>(topLevelNodes);
+        reachableStartNodes.retainAll(reachableNodes);
+        reachableStartNodes.remove(node);
+        for (N startNode : reachableStartNodes) {
+            reachableNodes.removeAll(calculateReachableNodes(nodes, startNode, topLevelNodes));
+        }
+        return reachableNodes;
+    }
+
+    public static class Result<N> {
+        private final Map<N, Set<N>> nodes;
+        private final Set<N> topLevelNodes;
+
+        public Result(Map<N, Set<N>> nodes, Set<N> topLevelNodes) {
+            this.nodes = nodes;
+            this.topLevelNodes = topLevelNodes;
+        }
+
+        public Set<N> getNodes(N startNode) {
+            return nodes.get(startNode);
+        }
+
+        public Set<N> getTopLevelNodes() {
+            return topLevelNodes;
+        }
+    }
+
+    private static class ConnectedNodesAsValuesDirectedGraph<N> implements DirectedGraph<N, N> {
+        private final DirectedGraph<N, ?> graph;
+
+        private ConnectedNodesAsValuesDirectedGraph(DirectedGraph<N, ?> graph) {
+            this.graph = graph;
+        }
+
+        public void getNodeValues(N node, Collection<? super N> values, Collection<? super N> connectedNodes) {
+            Set<N> edges = new LinkedHashSet<N>();
+            graph.getNodeValues(node, new ArrayList(), edges);
+            values.addAll(edges);
+            connectedNodes.addAll(edges);
+        }
+    }
+}
diff --git a/subprojects/core/src/main/groovy/org/gradle/internal/graph/GraphNodeRenderer.java b/subprojects/core/src/main/groovy/org/gradle/internal/graph/GraphNodeRenderer.java
new file mode 100644
index 0000000..e595847
--- /dev/null
+++ b/subprojects/core/src/main/groovy/org/gradle/internal/graph/GraphNodeRenderer.java
@@ -0,0 +1,22 @@
+/*
+ * Copyright 2013 the original author or authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.gradle.internal.graph;
+
+import org.gradle.logging.StyledTextOutput;
+
+public interface GraphNodeRenderer<N> {
+    void renderTo(N node, StyledTextOutput output);
+}
diff --git a/subprojects/core/src/main/groovy/org/gradle/internal/graph/GraphRenderer.java b/subprojects/core/src/main/groovy/org/gradle/internal/graph/GraphRenderer.java
new file mode 100644
index 0000000..09494dd
--- /dev/null
+++ b/subprojects/core/src/main/groovy/org/gradle/internal/graph/GraphRenderer.java
@@ -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.internal.graph;
+
+import org.gradle.api.Action;
+import org.gradle.logging.StyledTextOutput;
+
+import static org.gradle.logging.StyledTextOutput.Style.Info;
+
+public class GraphRenderer {
+    private final StyledTextOutput output;
+    private StringBuilder prefix = new StringBuilder();
+    private boolean seenRootChildren;
+    private boolean lastChild = true;
+
+    public GraphRenderer(StyledTextOutput output) {
+        this.output = output;
+    }
+
+    /**
+     * Visits a node in the graph.
+     */
+    public void visit(Action<? super StyledTextOutput> node, boolean lastChild) {
+        if (seenRootChildren) {
+            output.withStyle(Info).text(prefix + (lastChild ? "\\--- " : "+--- "));
+        }
+        this.lastChild = lastChild;
+        node.execute(output);
+        output.println();
+    }
+
+    /**
+     * Starts visiting the children of the most recently visited node.
+     */
+    public void startChildren() {
+        if (seenRootChildren) {
+            prefix.append(lastChild ? "     " : "|    ");
+        }
+        seenRootChildren = true;
+    }
+
+    /**
+     * Completes visiting the children of the node which most recently started visiting children.
+     */
+    public void completeChildren() {
+        if (prefix.length() == 0) {
+            seenRootChildren = false;
+        } else {
+            prefix.setLength(prefix.length() - 5);
+        }
+    }
+
+    public StyledTextOutput getOutput() {
+        return output;
+    }
+}
diff --git a/subprojects/core/src/main/groovy/org/gradle/internal/service/scopes/BuildScopeServices.java b/subprojects/core/src/main/groovy/org/gradle/internal/service/scopes/BuildScopeServices.java
new file mode 100644
index 0000000..30fa8b2
--- /dev/null
+++ b/subprojects/core/src/main/groovy/org/gradle/internal/service/scopes/BuildScopeServices.java
@@ -0,0 +1,265 @@
+/*
+ * Copyright 2010 the original author or authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.gradle.internal.service.scopes;
+
+import org.gradle.StartParameter;
+import org.gradle.api.Project;
+import org.gradle.api.artifacts.Module;
+import org.gradle.api.internal.*;
+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.classpath.ModuleRegistry;
+import org.gradle.api.internal.classpath.PluginModuleRegistry;
+import org.gradle.api.internal.file.FileResolver;
+import org.gradle.api.internal.initialization.DefaultScriptHandlerFactory;
+import org.gradle.api.internal.initialization.ScriptHandlerFactory;
+import org.gradle.api.internal.plugins.DefaultPluginRegistry;
+import org.gradle.api.internal.plugins.PluginRegistry;
+import org.gradle.api.internal.project.*;
+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.cache.CacheRepository;
+import org.gradle.cache.CacheValidator;
+import org.gradle.cache.internal.CacheFactory;
+import org.gradle.cache.internal.DefaultCacheRepository;
+import org.gradle.configuration.*;
+import org.gradle.configuration.project.*;
+import org.gradle.groovy.scripts.DefaultScriptCompilerFactory;
+import org.gradle.groovy.scripts.ScriptCompilerFactory;
+import org.gradle.groovy.scripts.ScriptExecutionListener;
+import org.gradle.groovy.scripts.internal.*;
+import org.gradle.initialization.*;
+import org.gradle.internal.Factory;
+import org.gradle.internal.TimeProvider;
+import org.gradle.internal.TrueTimeProvider;
+import org.gradle.internal.classloader.ClassLoaderFactory;
+import org.gradle.internal.concurrent.ExecutorFactory;
+import org.gradle.internal.id.LongIdGenerator;
+import org.gradle.internal.reflect.Instantiator;
+import org.gradle.internal.service.DefaultServiceRegistry;
+import org.gradle.internal.service.ServiceLocator;
+import org.gradle.internal.service.ServiceRegistry;
+import org.gradle.invocation.BuildClassLoaderRegistry;
+import org.gradle.invocation.DefaultBuildClassLoaderRegistry;
+import org.gradle.listener.ListenerManager;
+import org.gradle.logging.LoggingManagerInternal;
+import org.gradle.messaging.actor.ActorFactory;
+import org.gradle.messaging.actor.internal.DefaultActorFactory;
+import org.gradle.messaging.remote.MessagingServer;
+import org.gradle.process.internal.DefaultWorkerProcessFactory;
+import org.gradle.process.internal.WorkerProcessBuilder;
+import org.gradle.process.internal.child.WorkerProcessClassPathProvider;
+import org.gradle.profile.ProfileEventAdapter;
+import org.gradle.profile.ProfileListener;
+
+/**
+ * Contains the singleton services for a single build invocation.
+ */
+public class BuildScopeServices extends DefaultServiceRegistry implements ServiceRegistryFactory {
+    private final StartParameter startParameter;
+
+    public BuildScopeServices(final ServiceRegistry parent, final StartParameter startParameter) {
+        super(parent);
+        this.startParameter = startParameter;
+        add(StartParameter.class, startParameter);
+    }
+
+    protected ImportsReader createImportsReader() {
+        return new ImportsReader();
+    }
+
+    protected TimeProvider createTimeProvider() {
+        return new TrueTimeProvider();
+    }
+
+    protected IProjectFactory createProjectFactory() {
+        return new ProjectFactory(get(Instantiator.class));
+    }
+
+    protected ListenerManager createListenerManager(ListenerManager listenerManager) {
+        return listenerManager.createChild();
+    }
+
+    protected ClassPathRegistry createClassPathRegistry() {
+        return new DefaultClassPathRegistry(
+                new DefaultClassPathProvider(get(ModuleRegistry.class)),
+                new DependencyClassPathProvider(get(ModuleRegistry.class), get(PluginModuleRegistry.class)),
+                new WorkerProcessClassPathProvider(get(CacheRepository.class), get(ModuleRegistry.class)));
+    }
+
+    protected IsolatedAntBuilder createIsolatedAntBuilder() {
+        return new DefaultIsolatedAntBuilder(get(ClassPathRegistry.class), get(ClassLoaderFactory.class));
+    }
+
+    protected ActorFactory createActorFactory() {
+        return new DefaultActorFactory(get(ExecutorFactory.class));
+    }
+
+    protected IGradlePropertiesLoader createGradlePropertiesLoader() {
+        return new DefaultGradlePropertiesLoader(startParameter);
+    }
+
+    protected BuildLoader createBuildLoader() {
+        return new ProjectPropertySettingBuildLoader(
+                get(IGradlePropertiesLoader.class),
+                new InstantiatingBuildLoader(get(IProjectFactory.class)));
+    }
+
+    protected CacheFactory createCacheFactory() {
+        return getFactory(CacheFactory.class).create();
+    }
+
+    protected CacheRepository createCacheRepository() {
+        CacheFactory factory = get(CacheFactory.class);
+        return new DefaultCacheRepository(startParameter.getGradleUserHomeDir(), startParameter.getProjectCacheDir(),
+                startParameter.getCacheUsage(), factory);
+    }
+
+    protected ProjectEvaluator createProjectEvaluator() {
+        ConfigureActionsProjectEvaluator withActionsEvaluator = new ConfigureActionsProjectEvaluator(
+                new PluginsProjectConfigureActions(get(ClassLoaderRegistry.class).getPluginsClassLoader()),
+                new BuildScriptProcessor(get(ScriptPluginFactory.class)),
+                new DelayedConfigurationActions(),
+                new ProjectDependencies2TaskResolver()
+        );
+        return new LifecycleProjectEvaluator(withActionsEvaluator);
+    }
+
+    protected ITaskFactory createITaskFactory() {
+        return new DependencyAutoWireTaskFactory(
+                new AnnotationProcessingTaskFactory(
+                        new TaskFactory(
+                                get(ClassGenerator.class))));
+    }
+
+    protected BuildClassLoaderRegistry createBuildClassLoaderRegistry() {
+        return new DefaultBuildClassLoaderRegistry(get(ClassLoaderRegistry.class));
+    }
+
+    protected ScriptCompilerFactory createScriptCompileFactory() {
+        ScriptExecutionListener scriptExecutionListener = get(ListenerManager.class).getBroadcaster(ScriptExecutionListener.class);
+        EmptyScriptGenerator emptyScriptGenerator = new AsmBackedEmptyScriptGenerator();
+        CacheValidator scriptCacheInvalidator = new CacheValidator() {
+            public boolean isValid() {
+                return !get(StartParameter.class).isRecompileScripts();
+            }
+        };
+        return new DefaultScriptCompilerFactory(
+                new CachingScriptClassCompiler(
+                        new ShortCircuitEmptyScriptCompiler(
+                                new FileCacheBackedScriptClassCompiler(
+                                        get(CacheRepository.class),
+                                        scriptCacheInvalidator,
+                                        new DefaultScriptCompilationHandler(
+                                                emptyScriptGenerator)),
+                                emptyScriptGenerator)),
+                new DefaultScriptRunnerFactory(scriptExecutionListener));
+    }
+
+    protected ScriptPluginFactory createScriptObjectConfigurerFactory() {
+        return new DefaultScriptPluginFactory(
+                get(ScriptCompilerFactory.class),
+                get(ImportsReader.class),
+                get(ScriptHandlerFactory.class),
+                get(BuildClassLoaderRegistry.class).getScriptClassLoader(),
+                getFactory(LoggingManagerInternal.class),
+                get(Instantiator.class)
+        );
+    }
+
+    protected InitScriptHandler createInitScriptHandler() {
+        return new InitScriptHandler(
+                new DefaultInitScriptProcessor(get(ScriptPluginFactory.class))
+        );
+    }
+
+    protected SettingsProcessor createSettingsProcessor() {
+        return new PropertiesLoadingSettingsProcessor(
+                new ScriptEvaluatingSettingsProcessor(
+                        get(ScriptPluginFactory.class),
+                        new SettingsFactory(
+
+                                get(Instantiator.class),
+                                this
+                        ),
+                        get(IGradlePropertiesLoader.class)),
+                get(IGradlePropertiesLoader.class));
+    }
+
+    protected ExceptionAnalyser createExceptionAnalyser() {
+        return new MultipleBuildFailuresExceptionAnalyser(new DefaultExceptionAnalyser(get(ListenerManager.class)));
+    }
+
+    protected ScriptHandlerFactory createScriptHandlerFactory() {
+        return new DefaultScriptHandlerFactory(
+                get(DependencyManagementServices.class),
+                get(FileResolver.class),
+                new DependencyMetaDataProviderImpl());
+    }
+
+    protected Factory<WorkerProcessBuilder> createWorkerProcessFactory() {
+        ClassPathRegistry classPathRegistry = get(ClassPathRegistry.class);
+        return new DefaultWorkerProcessFactory(
+                startParameter.getLogLevel(),
+                get(MessagingServer.class),
+                classPathRegistry,
+                get(FileResolver.class),
+                new LongIdGenerator());
+    }
+
+    protected BuildConfigurer createBuildConfigurer() {
+        return new DefaultBuildConfigurer();
+    }
+
+    protected ProjectAccessListener createProjectAccessListener() {
+        return new DefaultProjectAccessListener();
+    }
+
+    protected ProfileEventAdapter createProfileEventAdapter() {
+        return new ProfileEventAdapter(get(BuildRequestMetaData.class), get(TimeProvider.class), get(ListenerManager.class).getBroadcaster(ProfileListener.class));
+    }
+
+    protected PluginRegistry createPluginRegistry() {
+        return new DefaultPluginRegistry(get(ClassLoaderRegistry.class).getPluginsClassLoader(), new DependencyInjectingInstantiator(this));
+    }
+
+    protected DependencyManagementServices createDependencyManagementServices() {
+        ClassLoader coreImplClassLoader = get(ClassLoaderRegistry.class).getCoreImplClassLoader();
+        ServiceLocator serviceLocator = new ServiceLocator(coreImplClassLoader);
+        return serviceLocator.getFactory(DependencyManagementServices.class).newInstance(this);
+    }
+
+    public ServiceRegistryFactory createFor(Object domainObject) {
+        if (domainObject instanceof GradleInternal) {
+            return new GradleScopeServices(this, (GradleInternal) domainObject);
+        }
+        if (domainObject instanceof SettingsInternal) {
+            return new SettingsScopeServices(this, (SettingsInternal) domainObject);
+        }
+        throw new IllegalArgumentException(String.format("Cannot create services for unknown domain object of type %s.",
+                domainObject.getClass().getSimpleName()));
+    }
+
+    private class DependencyMetaDataProviderImpl implements DependencyMetaDataProvider {
+        public Module getModule() {
+            return new DefaultModule("unspecified", "unspecified", Project.DEFAULT_VERSION, Project.DEFAULT_STATUS);
+        }
+    }
+}
diff --git a/subprojects/core/src/main/groovy/org/gradle/internal/service/scopes/GlobalScopeServices.java b/subprojects/core/src/main/groovy/org/gradle/internal/service/scopes/GlobalScopeServices.java
new file mode 100755
index 0000000..8d53ea7
--- /dev/null
+++ b/subprojects/core/src/main/groovy/org/gradle/internal/service/scopes/GlobalScopeServices.java
@@ -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.internal.service.scopes;
+
+import org.gradle.StartParameter;
+import org.gradle.api.internal.*;
+import org.gradle.api.internal.classpath.DefaultModuleRegistry;
+import org.gradle.api.internal.classpath.DefaultPluginModuleRegistry;
+import org.gradle.api.internal.classpath.ModuleRegistry;
+import org.gradle.api.internal.classpath.PluginModuleRegistry;
+import org.gradle.api.internal.file.FileResolver;
+import org.gradle.api.internal.file.IdentityFileResolver;
+import org.gradle.cache.internal.*;
+import org.gradle.cache.internal.locklistener.DefaultFileLockContentionHandler;
+import org.gradle.cache.internal.locklistener.FileLockContentionHandler;
+import org.gradle.cli.CommandLineConverter;
+import org.gradle.initialization.*;
+import org.gradle.internal.Factory;
+import org.gradle.internal.concurrent.DefaultExecutorFactory;
+import org.gradle.internal.concurrent.ExecutorFactory;
+import org.gradle.internal.nativeplatform.ProcessEnvironment;
+import org.gradle.internal.nativeplatform.filesystem.FileSystem;
+import org.gradle.internal.nativeplatform.services.NativeServices;
+import org.gradle.internal.reflect.DirectInstantiator;
+import org.gradle.internal.reflect.Instantiator;
+import org.gradle.internal.service.DefaultServiceRegistry;
+import org.gradle.internal.service.ServiceLocator;
+import org.gradle.internal.service.ServiceRegistry;
+import org.gradle.listener.DefaultListenerManager;
+import org.gradle.listener.ListenerManager;
+import org.gradle.logging.LoggingServiceRegistry;
+import org.gradle.messaging.remote.MessagingServer;
+import org.gradle.messaging.remote.internal.MessagingServices;
+import org.gradle.messaging.remote.internal.inet.InetAddressFactory;
+import org.gradle.internal.classloader.ClassLoaderFactory;
+import org.gradle.internal.classloader.DefaultClassLoaderFactory;
+
+import java.util.List;
+
+/**
+ * Contains the services shared by all builds in a given process.
+ */
+public class GlobalScopeServices extends DefaultServiceRegistry {
+    public GlobalScopeServices() {
+        this(LoggingServiceRegistry.newProcessLogging());
+    }
+
+    public GlobalScopeServices(ServiceRegistry loggingServices) {
+        super(loggingServices);
+        add(NativeServices.getInstance());
+        List<PluginServiceRegistry> pluginServiceFactories = new ServiceLocator(get(ClassLoaderRegistry.class).getRuntimeClassLoader()).getAll(PluginServiceRegistry.class);
+        for (PluginServiceRegistry pluginServiceRegistry : pluginServiceFactories) {
+            add(pluginServiceRegistry.createGlobalServices(this));
+        }
+    }
+
+    protected GradleLauncherFactory createGradleLauncherFactory() {
+        return new DefaultGradleLauncherFactory(this);
+    }
+
+    protected CommandLineConverter<StartParameter> createCommandLine2StartParameterConverter() {
+        return new DefaultCommandLineConverter();
+    }
+
+    protected ClassPathRegistry createClassPathRegistry() {
+        return new DefaultClassPathRegistry(new DefaultClassPathProvider(get(ModuleRegistry.class)), new DynamicModulesClassPathProvider(get(ModuleRegistry.class), get(PluginModuleRegistry.class)));
+    }
+
+    protected DefaultModuleRegistry createModuleRegistry() {
+        return new DefaultModuleRegistry();
+    }
+
+    protected DocumentationRegistry createDocumentationRegistry() {
+        return new DocumentationRegistry();
+    }
+
+    protected PluginModuleRegistry createPluginModuleRegistry() {
+        return new DefaultPluginModuleRegistry(get(ModuleRegistry.class));
+    }
+
+    protected Factory<CacheFactory> createCacheFactory() {
+        return new DefaultCacheFactory(get(FileLockManager.class));
+    }
+
+    protected ClassLoaderRegistry createClassLoaderRegistry() {
+        return new DefaultClassLoaderRegistry(get(ClassPathRegistry.class), get(ClassLoaderFactory.class));
+    }
+
+    protected ListenerManager createListenerManager() {
+        return new DefaultListenerManager();
+    }
+   
+    protected ClassLoaderFactory createClassLoaderFactory() {
+        return new DefaultClassLoaderFactory();
+    }
+
+    protected MessagingServices createMessagingServices() {
+        return new MessagingServices(get(ClassLoaderRegistry.class).getPluginsClassLoader());
+    }
+
+    protected MessagingServer createMessagingServer() {
+        return get(MessagingServices.class).get(MessagingServer.class);
+    }
+
+    protected ClassGenerator createClassGenerator() {
+        return new AsmBackedClassGenerator();
+    }
+
+    protected Instantiator createInstantiator() {
+        return new ClassGeneratorBackedInstantiator(get(ClassGenerator.class), new DirectInstantiator());
+    }
+
+    protected ExecutorFactory createExecutorFactory() {
+        return new DefaultExecutorFactory();
+    }
+
+    protected FileLockManager createFileLockManager() {
+        return new DefaultFileLockManager(
+                new DefaultProcessMetaDataProvider(
+                        get(ProcessEnvironment.class)),
+                get(FileLockContentionHandler.class));
+    }
+
+    private DefaultFileLockContentionHandler createFileLockContentionHandler() {
+        return new DefaultFileLockContentionHandler(
+                get(ExecutorFactory.class),
+                get(MessagingServices.class).get(InetAddressFactory.class)
+        );
+    }
+
+    private FileResolver createFileResolver() {
+        return new IdentityFileResolver(get(FileSystem.class));
+    }
+}
diff --git a/subprojects/core/src/main/groovy/org/gradle/internal/service/scopes/GradleScopeServices.java b/subprojects/core/src/main/groovy/org/gradle/internal/service/scopes/GradleScopeServices.java
new file mode 100644
index 0000000..3b268aa
--- /dev/null
+++ b/subprojects/core/src/main/groovy/org/gradle/internal/service/scopes/GradleScopeServices.java
@@ -0,0 +1,98 @@
+/*
+ * 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.internal.service.scopes;
+
+import org.gradle.StartParameter;
+import org.gradle.api.internal.DependencyInjectingInstantiator;
+import org.gradle.api.internal.GradleInternal;
+import org.gradle.api.internal.artifacts.dsl.dependencies.ProjectFinder;
+import org.gradle.api.internal.plugins.DefaultPluginContainer;
+import org.gradle.api.internal.plugins.PluginRegistry;
+import org.gradle.api.internal.project.DefaultProjectRegistry;
+import org.gradle.api.internal.project.ProjectInternal;
+import org.gradle.api.internal.project.ProjectRegistry;
+import org.gradle.api.plugins.PluginContainer;
+import org.gradle.execution.*;
+import org.gradle.execution.taskgraph.DefaultTaskGraphExecuter;
+import org.gradle.execution.taskgraph.TaskPlanExecutor;
+import org.gradle.internal.service.DefaultServiceRegistry;
+import org.gradle.internal.service.ServiceRegistry;
+import org.gradle.invocation.BuildClassLoaderRegistry;
+import org.gradle.listener.ListenerManager;
+
+import java.util.LinkedList;
+import java.util.List;
+
+import static java.util.Arrays.asList;
+
+/**
+ * Contains the services for a given {@link GradleInternal} instance.
+ */
+public class GradleScopeServices extends DefaultServiceRegistry implements ServiceRegistryFactory {
+    private final GradleInternal gradle;
+
+    public GradleScopeServices(ServiceRegistry parent, final GradleInternal gradle) {
+        super(parent);
+        this.gradle = gradle;
+        add(new TaskExecutionServices(parent, gradle));
+    }
+
+    protected BuildExecuter createBuildExecuter() {
+        List<BuildConfigurationAction> configs = new LinkedList<BuildConfigurationAction>();
+        if (get(StartParameter.class).isConfigureOnDemand()) {
+            configs.add(new ProjectEvaluatingAction());
+        }
+        configs.add(new DefaultTasksBuildExecutionAction());
+        configs.add(new ExcludedTaskFilteringBuildConfigurationAction());
+        configs.add(new TaskNameResolvingBuildConfigurationAction());
+
+        return new DefaultBuildExecuter(
+                configs,
+                asList(new DryRunBuildExecutionAction(),
+                       new SelectedTaskExecutionAction()));
+    }
+
+    protected ProjectFinder createProjectFinder() {
+        return new ProjectFinder() {
+            public ProjectInternal getProject(String path) {
+                return gradle.getRootProject().project(path);
+            }
+        };
+    }
+
+    protected ProjectRegistry createIProjectRegistry() {
+        return new DefaultProjectRegistry<ProjectInternal>();
+    }
+
+    protected TaskGraphExecuter createTaskGraphExecuter() {
+        return new DefaultTaskGraphExecuter(get(ListenerManager.class), get(TaskPlanExecutor.class));
+    }
+
+    public ServiceRegistryFactory createFor(Object domainObject) {
+        if (domainObject instanceof ProjectInternal) {
+            return new ProjectScopeServices(this, (ProjectInternal) domainObject);
+        }
+        throw new UnsupportedOperationException();
+    }
+
+    protected PluginRegistry createPluginRegistry(PluginRegistry parentRegistry) {
+        return parentRegistry.createChild(get(BuildClassLoaderRegistry.class).getScriptClassLoader(), new DependencyInjectingInstantiator(this));
+    }
+
+    protected PluginContainer createPluginContainer() {
+        return new DefaultPluginContainer(get(PluginRegistry.class), gradle);
+    }
+}
diff --git a/subprojects/core/src/main/groovy/org/gradle/internal/service/scopes/PluginServiceRegistry.java b/subprojects/core/src/main/groovy/org/gradle/internal/service/scopes/PluginServiceRegistry.java
new file mode 100644
index 0000000..4bd8bfe
--- /dev/null
+++ b/subprojects/core/src/main/groovy/org/gradle/internal/service/scopes/PluginServiceRegistry.java
@@ -0,0 +1,28 @@
+/*
+ * Copyright 2013 the original author or authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.gradle.internal.service.scopes;
+
+import org.gradle.internal.service.ServiceRegistry;
+
+/**
+ * Can be implemented by plugins to provide services in the global scope.
+ *
+ * <p>Implementations are discovered using the JAR service locator mechanism (see {@link org.gradle.internal.service.ServiceLocator}).
+ */
+public interface PluginServiceRegistry {
+    ServiceRegistry createGlobalServices(ServiceRegistry parent);
+}
diff --git a/subprojects/core/src/main/groovy/org/gradle/internal/service/scopes/ProjectScopeServices.java b/subprojects/core/src/main/groovy/org/gradle/internal/service/scopes/ProjectScopeServices.java
new file mode 100644
index 0000000..db108af
--- /dev/null
+++ b/subprojects/core/src/main/groovy/org/gradle/internal/service/scopes/ProjectScopeServices.java
@@ -0,0 +1,200 @@
+/*
+ * Copyright 2010 the original author or authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.gradle.internal.service.scopes;
+
+import org.gradle.api.AntBuilder;
+import org.gradle.api.artifacts.Module;
+import org.gradle.api.artifacts.dsl.ArtifactHandler;
+import org.gradle.api.artifacts.dsl.DependencyHandler;
+import org.gradle.api.artifacts.dsl.ComponentMetadataHandler;
+import org.gradle.api.artifacts.dsl.RepositoryHandler;
+import org.gradle.api.component.SoftwareComponentContainer;
+import org.gradle.api.internal.ClassGenerator;
+import org.gradle.api.internal.ClassGeneratorBackedInstantiator;
+import org.gradle.api.internal.DependencyInjectingInstantiator;
+import org.gradle.api.internal.TaskInternal;
+import org.gradle.api.internal.artifacts.ArtifactPublicationServices;
+import org.gradle.api.internal.artifacts.DependencyManagementServices;
+import org.gradle.api.internal.artifacts.DependencyResolutionServices;
+import org.gradle.api.internal.artifacts.ProjectBackedModule;
+import org.gradle.api.internal.artifacts.configurations.ConfigurationContainerInternal;
+import org.gradle.api.internal.artifacts.configurations.DependencyMetaDataProvider;
+import org.gradle.api.internal.artifacts.dsl.dependencies.ProjectFinder;
+import org.gradle.api.internal.component.DefaultSoftwareComponentContainer;
+import org.gradle.api.internal.file.*;
+import org.gradle.api.internal.initialization.DefaultScriptHandlerFactory;
+import org.gradle.api.internal.initialization.ScriptClassLoaderProvider;
+import org.gradle.api.internal.initialization.ScriptHandlerFactory;
+import org.gradle.api.internal.initialization.ScriptHandlerInternal;
+import org.gradle.api.internal.plugins.DefaultPluginContainer;
+import org.gradle.api.internal.plugins.PluginRegistry;
+import org.gradle.api.internal.project.DefaultAntBuilderFactory;
+import org.gradle.api.internal.project.ProjectInternal;
+import org.gradle.api.internal.project.ant.AntLoggingAdapter;
+import org.gradle.api.internal.project.taskfactory.ITaskFactory;
+import org.gradle.api.internal.tasks.DefaultTaskContainerFactory;
+import org.gradle.api.internal.tasks.TaskContainerInternal;
+import org.gradle.api.plugins.PluginContainer;
+import org.gradle.configuration.project.DefaultProjectConfigurationActionContainer;
+import org.gradle.configuration.project.ProjectConfigurationActionContainer;
+import org.gradle.initialization.ProjectAccessListener;
+import org.gradle.internal.Factory;
+import org.gradle.internal.nativeplatform.filesystem.FileSystem;
+import org.gradle.internal.reflect.Instantiator;
+import org.gradle.internal.service.DefaultServiceRegistry;
+import org.gradle.internal.service.ServiceRegistry;
+import org.gradle.invocation.BuildClassLoaderRegistry;
+import org.gradle.logging.LoggingManagerInternal;
+import org.gradle.tooling.provider.model.ToolingModelBuilderRegistry;
+import org.gradle.tooling.provider.model.internal.DefaultToolingModelBuilderRegistry;
+
+import java.io.File;
+
+/**
+ * Contains the services for a given project.
+ */
+public class ProjectScopeServices extends DefaultServiceRegistry implements ServiceRegistryFactory {
+    private final ProjectInternal project;
+
+    public ProjectScopeServices(ServiceRegistry parent, final ProjectInternal project) {
+        super(parent);
+        this.project = project;
+    }
+
+    protected PluginRegistry createPluginRegistry(PluginRegistry parentRegistry) {
+        return parentRegistry.createChild(get(ScriptClassLoaderProvider.class).getClassLoader(), new DependencyInjectingInstantiator(this));
+    }
+
+    protected FileResolver createFileResolver() {
+        return new BaseDirFileResolver(get(FileSystem.class), project.getProjectDir());
+    }
+
+    protected LoggingManagerInternal createLoggingManager() {
+        return getFactory(LoggingManagerInternal.class).create();
+    }
+
+    protected ProjectConfigurationActionContainer createProjectConfigurationActionContainer() {
+        return new DefaultProjectConfigurationActionContainer();
+    }
+
+    protected DefaultFileOperations createFileOperations() {
+        return new DefaultFileOperations(get(FileResolver.class), project.getTasks(), get(TemporaryFileProvider.class), get(Instantiator.class));
+    }
+
+    protected TemporaryFileProvider createTemporaryFileProvider() {
+        return new DefaultTemporaryFileProvider(new Factory<File>() {
+            public File create() {
+                return new File(project.getBuildDir(), "tmp");
+            }
+        });
+    }
+
+    protected Factory<AntBuilder> createAntBuilderFactory() {
+        return new DefaultAntBuilderFactory(new AntLoggingAdapter(), project);
+    }
+
+    protected ToolingModelBuilderRegistry createToolingModelRegistry() {
+        return new DefaultToolingModelBuilderRegistry();
+    }
+
+    protected PluginContainer createPluginContainer() {
+        return new DefaultPluginContainer(get(PluginRegistry.class), project);
+    }
+
+    protected ITaskFactory createTaskFactory(ITaskFactory parentFactory) {
+        return parentFactory.createChild(project, new ClassGeneratorBackedInstantiator(get(ClassGenerator.class), new DependencyInjectingInstantiator(this)));
+    }
+
+    protected Factory<TaskContainerInternal> createTaskContainerInternal() {
+        return new DefaultTaskContainerFactory(get(Instantiator.class), get(ITaskFactory.class), project, get(ProjectAccessListener.class));
+    }
+
+    protected ArtifactPublicationServices createArtifactPublicationServices() {
+        return get(DependencyResolutionServices.class).createArtifactPublicationServices();
+    }
+
+    protected RepositoryHandler createRepositoryHandler() {
+        return get(DependencyResolutionServices.class).getResolveRepositoryHandler();
+    }
+
+    protected ConfigurationContainerInternal createConfigurationContainer() {
+        return get(DependencyResolutionServices.class).getConfigurationContainer();
+    }
+
+    protected SoftwareComponentContainer createSoftwareComponentContainer() {
+        Instantiator instantiator = get(Instantiator.class);
+        return instantiator.newInstance(DefaultSoftwareComponentContainer.class, instantiator);
+    }
+
+    protected DependencyResolutionServices createDependencyResolutionServices() {
+        return newDependencyResolutionServices();
+    }
+
+    private DependencyResolutionServices newDependencyResolutionServices() {
+        return get(DependencyManagementServices.class).create(get(FileResolver.class), get(DependencyMetaDataProvider.class), get(ProjectFinder.class), project);
+    }
+
+    protected ArtifactHandler createArtifactHandler() {
+        return get(DependencyResolutionServices.class).getArtifactHandler();
+    }
+
+    protected ProjectFinder createProjectFinder() {
+        return new ProjectFinder() {
+            public ProjectInternal getProject(String path) {
+                return project.project(path);
+            }
+        };
+    }
+
+    protected DependencyHandler createDependencyHandler() {
+        return get(DependencyResolutionServices.class).getDependencyHandler();
+    }
+
+    protected ComponentMetadataHandler createModuleHandler() {
+        return get(DependencyResolutionServices.class).getComponentMetadataHandler();
+    }
+
+    protected ScriptHandlerInternal createScriptHandler() {
+        ScriptHandlerFactory factory = new DefaultScriptHandlerFactory(
+                get(DependencyManagementServices.class),
+                get(FileResolver.class),
+                get(DependencyMetaDataProvider.class));
+        ClassLoader parentClassLoader;
+        if (project.getParent() != null) {
+            parentClassLoader = project.getParent().getBuildscript().getClassLoader();
+        } else {
+            parentClassLoader = get(BuildClassLoaderRegistry.class).getScriptClassLoader();
+        }
+        return factory.create(project.getBuildScriptSource(), parentClassLoader, project);
+    }
+
+    protected DependencyMetaDataProvider createDependencyMetaDataProvider() {
+        return new DependencyMetaDataProvider() {
+            public Module getModule() {
+                return new ProjectBackedModule(project);
+            }
+        };
+    }
+
+    public ServiceRegistryFactory createFor(Object domainObject) {
+        if (domainObject instanceof TaskInternal) {
+            return new TaskScopeServices(this, project, (TaskInternal)domainObject);
+        }
+        throw new UnsupportedOperationException();
+    }
+
+}
diff --git a/subprojects/core/src/main/groovy/org/gradle/internal/service/scopes/ServiceRegistryFactory.java b/subprojects/core/src/main/groovy/org/gradle/internal/service/scopes/ServiceRegistryFactory.java
new file mode 100644
index 0000000..2749b70
--- /dev/null
+++ b/subprojects/core/src/main/groovy/org/gradle/internal/service/scopes/ServiceRegistryFactory.java
@@ -0,0 +1,31 @@
+/*
+ * 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.internal.service.scopes;
+
+import org.gradle.internal.service.ServiceRegistry;
+
+/**
+ * A hierarchical service registry.
+ */
+public interface ServiceRegistryFactory extends ServiceRegistry {
+    /**
+     * Creates the services for the given domain object.
+     *
+     * @param domainObject The domain object.
+     * @return The registry containing the services for the domain object.
+     */
+    ServiceRegistryFactory createFor(Object domainObject);
+}
diff --git a/subprojects/core/src/main/groovy/org/gradle/internal/service/scopes/SettingsScopeServices.java b/subprojects/core/src/main/groovy/org/gradle/internal/service/scopes/SettingsScopeServices.java
new file mode 100644
index 0000000..91306ea
--- /dev/null
+++ b/subprojects/core/src/main/groovy/org/gradle/internal/service/scopes/SettingsScopeServices.java
@@ -0,0 +1,59 @@
+/*
+ * Copyright 2013 the original author or authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.gradle.internal.service.scopes;
+
+import org.gradle.api.internal.DependencyInjectingInstantiator;
+import org.gradle.api.internal.SettingsInternal;
+import org.gradle.api.internal.file.BaseDirFileResolver;
+import org.gradle.api.internal.file.FileResolver;
+import org.gradle.api.internal.plugins.DefaultPluginContainer;
+import org.gradle.api.internal.plugins.PluginRegistry;
+import org.gradle.api.plugins.PluginContainer;
+import org.gradle.initialization.DefaultProjectDescriptorRegistry;
+import org.gradle.initialization.ProjectDescriptorRegistry;
+import org.gradle.internal.nativeplatform.filesystem.FileSystem;
+import org.gradle.internal.service.DefaultServiceRegistry;
+import org.gradle.internal.service.ServiceRegistry;
+
+public class SettingsScopeServices extends DefaultServiceRegistry implements ServiceRegistryFactory {
+    private final SettingsInternal settings;
+
+    public SettingsScopeServices(ServiceRegistry parent, final SettingsInternal settings) {
+        super(parent);
+        this.settings = settings;
+    }
+
+    public ServiceRegistryFactory createFor(Object domainObject) {
+        throw new UnsupportedOperationException();
+    }
+
+    protected FileResolver createFileResolver() {
+        return new BaseDirFileResolver(get(FileSystem.class), settings.getSettingsDir());
+    }
+
+    protected PluginRegistry createPluginRegistry(PluginRegistry parentRegistry) {
+        return parentRegistry.createChild(settings.getClassLoader(), new DependencyInjectingInstantiator(this));
+    }
+
+    protected PluginContainer createPluginContainer() {
+        return new DefaultPluginContainer(get(PluginRegistry.class), settings);
+    }
+
+    protected ProjectDescriptorRegistry createProjectDescriptorRegistry() {
+        return new DefaultProjectDescriptorRegistry();
+    }
+}
diff --git a/subprojects/core/src/main/groovy/org/gradle/internal/service/scopes/TaskExecutionServices.java b/subprojects/core/src/main/groovy/org/gradle/internal/service/scopes/TaskExecutionServices.java
new file mode 100644
index 0000000..7cba668
--- /dev/null
+++ b/subprojects/core/src/main/groovy/org/gradle/internal/service/scopes/TaskExecutionServices.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.internal.service.scopes;
+
+import org.gradle.StartParameter;
+import org.gradle.api.execution.TaskActionListener;
+import org.gradle.api.internal.changedetection.TaskArtifactStateRepository;
+import org.gradle.api.internal.changedetection.changes.DefaultTaskArtifactStateRepository;
+import org.gradle.api.internal.changedetection.changes.ShortCircuitTaskArtifactStateRepository;
+import org.gradle.api.internal.changedetection.state.*;
+import org.gradle.api.internal.tasks.TaskExecuter;
+import org.gradle.api.internal.tasks.execution.*;
+import org.gradle.api.invocation.Gradle;
+import org.gradle.cache.CacheRepository;
+import org.gradle.execution.taskgraph.TaskPlanExecutor;
+import org.gradle.execution.taskgraph.TaskPlanExecutorFactory;
+import org.gradle.internal.id.RandomLongIdGenerator;
+import org.gradle.internal.reflect.Instantiator;
+import org.gradle.internal.service.DefaultServiceRegistry;
+import org.gradle.internal.service.ServiceRegistry;
+import org.gradle.listener.ListenerManager;
+
+public class TaskExecutionServices extends DefaultServiceRegistry {
+    private final Gradle gradle;
+
+    public TaskExecutionServices(ServiceRegistry parent, Gradle gradle) {
+        super(parent);
+        this.gradle = gradle;
+    }
+
+    protected TaskExecuter createTaskExecuter() {
+        TaskArtifactStateCacheAccess cacheAccess = get(TaskArtifactStateCacheAccess.class);
+        TaskArtifactStateRepository repository = get(TaskArtifactStateRepository.class);
+        return new ExecuteAtMostOnceTaskExecuter(
+                new SkipOnlyIfTaskExecuter(
+                        new SkipTaskWithNoActionsExecuter(
+                                new SkipEmptySourceFilesTaskExecuter(
+                                        new ValidatingTaskExecuter(
+                                                new SkipUpToDateTaskExecuter(repository,
+                                                        new PostExecutionAnalysisTaskExecuter(
+                                                                new ExecuteActionsTaskExecuter(
+                                                                        get(ListenerManager.class).getBroadcaster(TaskActionListener.class)
+                                                                ))))))));
+    }
+
+    protected TaskArtifactStateCacheAccess createCacheAccess() {
+        return new DefaultTaskArtifactStateCacheAccess(gradle, get(CacheRepository.class));
+    }
+
+    protected TaskArtifactStateRepository createTaskArtifactStateRepository() {
+        TaskArtifactStateCacheAccess cacheAccess = get(TaskArtifactStateCacheAccess.class);
+
+        FileSnapshotter fileSnapshotter = new DefaultFileSnapshotter(
+                new CachingHasher(
+                        new DefaultHasher(),
+                        cacheAccess), cacheAccess);
+
+        FileSnapshotter outputFilesSnapshotter = new OutputFilesSnapshotter(fileSnapshotter, new RandomLongIdGenerator(), cacheAccess);
+
+        TaskHistoryRepository taskHistoryRepository = new CacheBackedTaskHistoryRepository(cacheAccess, new CacheBackedFileSnapshotRepository(cacheAccess, new RandomLongIdGenerator()));
+
+        Instantiator instantiator = get(Instantiator.class);
+        return new ShortCircuitTaskArtifactStateRepository(
+                        get(StartParameter.class),
+                        instantiator,
+                        new DefaultTaskArtifactStateRepository(
+                                taskHistoryRepository,
+                                instantiator,
+                                outputFilesSnapshotter,
+                                fileSnapshotter
+                        )
+        );
+    }
+
+    protected TaskPlanExecutor createTaskExecutorFactory() {
+        StartParameter startParameter = gradle.getStartParameter();
+        TaskArtifactStateCacheAccess cacheAccess = get(TaskArtifactStateCacheAccess.class);
+        return new TaskPlanExecutorFactory(cacheAccess, startParameter.getParallelThreadCount()).create();
+    }
+}
diff --git a/subprojects/core/src/main/groovy/org/gradle/internal/service/scopes/TaskScopeServices.java b/subprojects/core/src/main/groovy/org/gradle/internal/service/scopes/TaskScopeServices.java
new file mode 100644
index 0000000..2c59b78
--- /dev/null
+++ b/subprojects/core/src/main/groovy/org/gradle/internal/service/scopes/TaskScopeServices.java
@@ -0,0 +1,62 @@
+/*
+ * Copyright 2010 the original author or authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.gradle.internal.service.scopes;
+
+import org.gradle.api.internal.TaskInternal;
+import org.gradle.api.internal.TaskOutputsInternal;
+import org.gradle.api.internal.project.ProjectInternal;
+import org.gradle.api.internal.tasks.DefaultTaskInputs;
+import org.gradle.api.internal.tasks.DefaultTaskOutputs;
+import org.gradle.api.internal.tasks.TaskStatusNagger;
+import org.gradle.api.tasks.TaskInputs;
+import org.gradle.internal.service.DefaultServiceRegistry;
+import org.gradle.internal.service.ServiceRegistry;
+import org.gradle.logging.LoggingManagerInternal;
+
+/**
+ * Contains the services for a given task.
+ */
+public class TaskScopeServices extends DefaultServiceRegistry implements ServiceRegistryFactory {
+    private final ProjectInternal project;
+    private final TaskInternal taskInternal;
+
+    public TaskScopeServices(ServiceRegistry parent, final ProjectInternal project, TaskInternal taskInternal) {
+        super(parent);
+        this.project = project;
+        this.taskInternal = taskInternal;
+    }
+
+    protected TaskInputs createTaskInputs() {
+        return new DefaultTaskInputs(project.getFileResolver(), taskInternal, get(TaskStatusNagger.class));
+    }
+
+    protected TaskOutputsInternal createTaskOutputs() {
+        return new DefaultTaskOutputs(project.getFileResolver(), taskInternal, get(TaskStatusNagger.class));
+    }
+
+    protected TaskStatusNagger createTaskStatusNagger() {
+        return new TaskStatusNagger(taskInternal);
+    }
+
+    protected LoggingManagerInternal createLoggingManager() {
+        return getFactory(LoggingManagerInternal.class).create();
+    }
+
+    public ServiceRegistryFactory createFor(Object domainObject) {
+        throw new UnsupportedOperationException();
+    }
+}
diff --git a/subprojects/core/src/main/groovy/org/gradle/invocation/BuildClassLoaderRegistry.java b/subprojects/core/src/main/groovy/org/gradle/invocation/BuildClassLoaderRegistry.java
new file mode 100644
index 0000000..c9399bf
--- /dev/null
+++ b/subprojects/core/src/main/groovy/org/gradle/invocation/BuildClassLoaderRegistry.java
@@ -0,0 +1,32 @@
+/*
+ * Copyright 2013 the original author or authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.gradle.invocation;
+
+public interface BuildClassLoaderRegistry {
+    /**
+     * Registers a {@code ClassLoader} to make visible to all scripts.
+     */
+    void addRootClassLoader(ClassLoader classLoader);
+
+    /**
+     * Returns the root {@code ClassLoader} to use for all scripts, including init and settings scripts. This {@code ClassLoader} exposes the Gradle API
+     * plus any classes that are exposed using {@link #addRootClassLoader(ClassLoader)}.
+     *
+     * <p>This {@code ClassLoader} is also used to locate plugins by id.</p>
+     */
+    ClassLoader getScriptClassLoader();
+}
diff --git a/subprojects/core/src/main/groovy/org/gradle/invocation/DefaultBuildClassLoaderRegistry.java b/subprojects/core/src/main/groovy/org/gradle/invocation/DefaultBuildClassLoaderRegistry.java
new file mode 100644
index 0000000..9b47683
--- /dev/null
+++ b/subprojects/core/src/main/groovy/org/gradle/invocation/DefaultBuildClassLoaderRegistry.java
@@ -0,0 +1,39 @@
+/*
+ * Copyright 2013 the original author or authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.gradle.invocation;
+
+import org.gradle.initialization.ClassLoaderRegistry;
+import org.gradle.internal.classloader.CachingClassLoader;
+import org.gradle.internal.classloader.MultiParentClassLoader;
+
+public class DefaultBuildClassLoaderRegistry implements BuildClassLoaderRegistry {
+    private final MultiParentClassLoader rootClassLoader;
+    private final CachingClassLoader scriptClassLoader;
+
+    public DefaultBuildClassLoaderRegistry(ClassLoaderRegistry registry) {
+        rootClassLoader = new MultiParentClassLoader(registry.getGradleApiClassLoader());
+        scriptClassLoader = new CachingClassLoader(rootClassLoader);
+    }
+
+    public void addRootClassLoader(ClassLoader classLoader) {
+        rootClassLoader.addParent(classLoader);
+    }
+
+    public ClassLoader getScriptClassLoader() {
+        return scriptClassLoader;
+    }
+}
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 9b21679..2bd4cee 100644
--- a/subprojects/core/src/main/groovy/org/gradle/invocation/DefaultGradle.java
+++ b/subprojects/core/src/main/groovy/org/gradle/invocation/DefaultGradle.java
@@ -25,28 +25,30 @@ import org.gradle.api.Project;
 import org.gradle.api.ProjectEvaluationListener;
 import org.gradle.api.internal.GradleDistributionLocator;
 import org.gradle.api.internal.GradleInternal;
-import org.gradle.api.internal.project.IProjectRegistry;
+import org.gradle.api.internal.file.FileResolver;
+import org.gradle.api.internal.project.AbstractPluginAware;
 import org.gradle.api.internal.project.ProjectInternal;
-import org.gradle.api.internal.project.ServiceRegistryFactory;
+import org.gradle.api.internal.project.ProjectRegistry;
+import org.gradle.internal.service.scopes.ServiceRegistryFactory;
 import org.gradle.api.invocation.Gradle;
+import org.gradle.api.plugins.PluginContainer;
+import org.gradle.configuration.ScriptPluginFactory;
 import org.gradle.execution.TaskGraphExecuter;
 import org.gradle.listener.ActionBroadcast;
 import org.gradle.listener.ClosureBackedMethodInvocationDispatch;
 import org.gradle.listener.ListenerBroadcast;
 import org.gradle.listener.ListenerManager;
 import org.gradle.util.GradleVersion;
-import org.gradle.util.MultiParentClassLoader;
 
 import java.io.File;
 
-public class DefaultGradle implements GradleInternal {
+public class DefaultGradle extends AbstractPluginAware implements GradleInternal {
     private ProjectInternal rootProject;
     private ProjectInternal defaultProject;
-    private TaskGraphExecuter taskGraph;
+    private final TaskGraphExecuter taskGraph;
     private final Gradle parent;
-    private StartParameter startParameter;
-    private MultiParentClassLoader scriptClassLoader;
-    private IProjectRegistry<ProjectInternal> projectRegistry;
+    private final StartParameter startParameter;
+    private final ProjectRegistry<ProjectInternal> projectRegistry;
     private final ListenerManager listenerManager;
     private final ServiceRegistryFactory services;
     private final GradleDistributionLocator distributionLocator;
@@ -54,18 +56,24 @@ public class DefaultGradle implements GradleInternal {
     private final ListenerBroadcast<ProjectEvaluationListener> projectEvaluationListenerBroadcast;
     private ActionBroadcast<Project> rootProjectActions = new ActionBroadcast<Project>();
 
+    private PluginContainer pluginContainer;
+    private FileResolver fileResolver;
+    private ScriptPluginFactory scriptPluginFactory;
+
     public DefaultGradle(Gradle parent, StartParameter startParameter, ServiceRegistryFactory parentRegistry) {
         this.parent = parent;
         this.startParameter = startParameter;
         this.services = parentRegistry.createFor(this);
         this.listenerManager = services.get(ListenerManager.class);
-        projectRegistry = services.get(IProjectRegistry.class);
+        projectRegistry = services.get(ProjectRegistry.class);
         taskGraph = services.get(TaskGraphExecuter.class);
-        scriptClassLoader = services.get(MultiParentClassLoader.class);
         distributionLocator = services.get(GradleDistributionLocator.class);
+        pluginContainer = services.get(PluginContainer.class);
+        fileResolver = services.get(FileResolver.class);
+        scriptPluginFactory = services.get(ScriptPluginFactory.class);
         buildListenerBroadcast = listenerManager.createAnonymousBroadcaster(BuildListener.class);
         projectEvaluationListenerBroadcast = listenerManager.createAnonymousBroadcaster(ProjectEvaluationListener.class);
-        buildListenerBroadcast.add(new BuildAdapter(){
+        buildListenerBroadcast.add(new BuildAdapter() {
             @Override
             public void projectsLoaded(Gradle gradle) {
                 rootProjectActions.execute(rootProject);
@@ -139,18 +147,10 @@ public class DefaultGradle implements GradleInternal {
         return taskGraph;
     }
 
-    public void setTaskGraph(TaskGraphExecuter taskGraph) {
-        this.taskGraph = taskGraph;
-    }
-
-    public IProjectRegistry<ProjectInternal> getProjectRegistry() {
+    public ProjectRegistry<ProjectInternal> getProjectRegistry() {
         return projectRegistry;
     }
 
-    public MultiParentClassLoader getScriptClassLoader() {
-        return scriptClassLoader;
-    }
-
     public ProjectEvaluationListener addProjectEvaluationListener(ProjectEvaluationListener listener) {
         addListener(listener);
         return listener;
@@ -219,4 +219,18 @@ public class DefaultGradle implements GradleInternal {
     public ServiceRegistryFactory getServices() {
         return services;
     }
+
+    public PluginContainer getPlugins() {
+        return pluginContainer;
+    }
+
+    @Override
+    protected FileResolver getFileResolver() {
+        return fileResolver;
+    }
+
+    @Override
+    protected ScriptPluginFactory getScriptPluginFactory() {
+        return scriptPluginFactory;
+    }
 }
diff --git a/subprojects/core/src/main/groovy/org/gradle/logging/StandardOutputCapture.java b/subprojects/core/src/main/groovy/org/gradle/logging/StandardOutputCapture.java
index 8392d98..316b47a 100644
--- a/subprojects/core/src/main/groovy/org/gradle/logging/StandardOutputCapture.java
+++ b/subprojects/core/src/main/groovy/org/gradle/logging/StandardOutputCapture.java
@@ -15,9 +15,6 @@
  */
 package org.gradle.logging;
 
-/**
- * @author Hans Dockter
- */
 public interface StandardOutputCapture {
     /**
      * Starts redirection of System.out and System.err to the Gradle logging system.
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 3745344..a729a18 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
@@ -25,9 +25,6 @@ import org.gradle.logging.LoggingManagerInternal;
 import java.util.LinkedHashSet;
 import java.util.Set;
 
-/**
- * @author Hans Dockter
- */
 public class DefaultLoggingManager implements LoggingManagerInternal {
     private boolean started;
     private final StartableLoggingSystem loggingSystem;
diff --git a/subprojects/core/src/main/groovy/org/gradle/logging/internal/EmbeddedLoggingServices.java b/subprojects/core/src/main/groovy/org/gradle/logging/internal/EmbeddedLoggingServices.java
index e791f36..f98be66 100644
--- a/subprojects/core/src/main/groovy/org/gradle/logging/internal/EmbeddedLoggingServices.java
+++ b/subprojects/core/src/main/groovy/org/gradle/logging/internal/EmbeddedLoggingServices.java
@@ -19,9 +19,6 @@ package org.gradle.logging.internal;
 import org.gradle.internal.Factory;
 import org.gradle.logging.LoggingManagerInternal;
 
-/**
- * by Szczepan Faber, created at: 1/23/12
- */
 public interface EmbeddedLoggingServices {
 
     Factory<LoggingManagerInternal> getLoggingManagerFactory();
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 3848666..9ba8e82 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
@@ -25,9 +25,9 @@ import org.gradle.cli.ParsedCommandLine;
 import org.gradle.logging.LoggingConfiguration;
 import org.gradle.logging.ShowStacktrace;
 
-import java.util.Collection;
-import java.util.Collections;
+import java.util.Arrays;
 import java.util.HashSet;
+import java.util.Map;
 import java.util.Set;
 
 public class LoggingCommandLineConverter extends AbstractCommandLineConverter<LoggingConfiguration> {
@@ -49,7 +49,6 @@ public class LoggingCommandLineConverter extends AbstractCommandLineConverter<Lo
         logLevelMap.put(QUIET, LogLevel.QUIET);
         logLevelMap.put(INFO, LogLevel.INFO);
         logLevelMap.put(DEBUG, LogLevel.DEBUG);
-        logLevelMap.put("", LogLevel.LIFECYCLE);
         showStacktraceMap.put(FULL_STACKTRACE, ShowStacktrace.ALWAYS_FULL);
         showStacktraceMap.put(STACKTRACE, ShowStacktrace.ALWAYS);
     }
@@ -60,45 +59,36 @@ public class LoggingCommandLineConverter extends AbstractCommandLineConverter<Lo
     }
 
     public LoggingConfiguration convert(ParsedCommandLine commandLine, LoggingConfiguration loggingConfiguration) throws CommandLineArgumentException {
-        loggingConfiguration.setLogLevel(getLogLevel(commandLine));
-        if (commandLine.hasOption(NO_COLOR)) {
-            loggingConfiguration.setColorOutput(false);
+        for (Map.Entry<String, LogLevel> entry : logLevelMap.entrySet()) {
+            if (commandLine.hasOption(entry.getKey())) {
+                loggingConfiguration.setLogLevel(entry.getValue());
+            }
         }
-        loggingConfiguration.setShowStacktrace(getShowStacktrace(commandLine));
-        return loggingConfiguration;
-    }
 
-    private ShowStacktrace getShowStacktrace(ParsedCommandLine options) {
-        if (options.hasOption(FULL_STACKTRACE)) {
-            return ShowStacktrace.ALWAYS_FULL;
+        for (Map.Entry<String, ShowStacktrace> entry : showStacktraceMap.entrySet()) {
+            if (commandLine.hasOption(entry.getKey())) {
+                loggingConfiguration.setShowStacktrace(entry.getValue());
+            }
         }
-        if (options.hasOption(STACKTRACE)) {
-            return ShowStacktrace.ALWAYS;
-        }
-        return ShowStacktrace.INTERNAL_EXCEPTIONS;
-    }
 
-    private LogLevel getLogLevel(ParsedCommandLine options) {
-        LogLevel logLevel = LogLevel.LIFECYCLE;
-        if (options.hasOption(QUIET)) {
-            logLevel = LogLevel.QUIET;
-        }
-        if (options.hasOption(INFO)) {
-            logLevel = LogLevel.INFO;
-        }
-        if (options.hasOption(DEBUG)) {
-            logLevel = LogLevel.DEBUG;
+        if (commandLine.hasOption(NO_COLOR)) {
+            loggingConfiguration.setColorOutput(false);
         }
-        return logLevel;
+
+        return loggingConfiguration;
     }
 
     public void configure(CommandLineParser parser) {
         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.allowOneOf(DEBUG, QUIET, INFO);
+
         parser.option(NO_COLOR).hasDescription("Do not use color in the console output.");
+
         parser.option(STACKTRACE, STACKTRACE_LONG).hasDescription("Print out the stacktrace for all exceptions.");
         parser.option(FULL_STACKTRACE, FULL_STACKTRACE_LONG).hasDescription("Print out the full (very verbose) stacktrace for all exceptions.");
+        parser.allowOneOf(STACKTRACE, FULL_STACKTRACE_LONG);
     }
 
     /**
@@ -106,7 +96,6 @@ public class LoggingCommandLineConverter extends AbstractCommandLineConverter<Lo
      *
      * @param commandLineArgument a single command line argument (with no '-')
      * @return the corresponding log level or null if it doesn't match any.
-     * @author mhunsicker
      */
     public LogLevel getLogLevel(String commandLineArgument) {
         LogLevel logLevel = logLevelMap.get(commandLineArgument);
@@ -122,7 +111,6 @@ public class LoggingCommandLineConverter extends AbstractCommandLineConverter<Lo
      *
      * @param logLevel the log level.
      * @return the command line argument or null if this level cannot be represented on the command line.
-     * @author mhunsicker
      */
     public String getLogLevelCommandLine(LogLevel logLevel) {
         String commandLine = logLevelMap.inverse().get(logLevel);
@@ -137,19 +125,16 @@ public class LoggingCommandLineConverter extends AbstractCommandLineConverter<Lo
      * This returns the log levels that are supported on the command line.
      *
      * @return a collection of available log levels
-     * @author mhunsicker
      */
-    public Collection<LogLevel> getLogLevels() {
-        return Collections.unmodifiableCollection(logLevelMap.values());
+    public Set<LogLevel> getLogLevels() {
+        return new HashSet<LogLevel>(Arrays.asList(LogLevel.DEBUG, LogLevel.INFO, LogLevel.LIFECYCLE, LogLevel.QUIET));
     }
 
     /**
      * @return the set of short option strings that are used to configure log levels.
      */
     public Set<String> getLogLevelOptions() {
-        Set<String> options = new HashSet<String>(logLevelMap.keySet());
-        options.remove("");
-        return options;
+        return logLevelMap.keySet();
     }
 
     /**
@@ -157,7 +142,6 @@ public class LoggingCommandLineConverter extends AbstractCommandLineConverter<Lo
      *
      * @param commandLineArgument a single command line argument (with no '-')
      * @return the corresponding stack trace level or null if it doesn't match any.
-     * @author mhunsicker
      */
     public ShowStacktrace getShowStacktrace(String commandLineArgument) {
         ShowStacktrace showStacktrace = showStacktraceMap.get(commandLineArgument);
@@ -173,7 +157,6 @@ public class LoggingCommandLineConverter extends AbstractCommandLineConverter<Lo
      *
      * @param showStacktrace the stack trace level.
      * @return the command line argument or null if this level cannot be represented on the command line.
-     * @author mhunsicker
      */
     public String getShowStacktraceCommandLine(ShowStacktrace showStacktrace) {
         String commandLine = showStacktraceMap.inverse().get(showStacktrace);
diff --git a/subprojects/core/src/main/groovy/org/gradle/logging/internal/LoggingConfigurer.java b/subprojects/core/src/main/groovy/org/gradle/logging/internal/LoggingConfigurer.java
index 6553da8..ceec51e 100644
--- a/subprojects/core/src/main/groovy/org/gradle/logging/internal/LoggingConfigurer.java
+++ b/subprojects/core/src/main/groovy/org/gradle/logging/internal/LoggingConfigurer.java
@@ -17,9 +17,6 @@ package org.gradle.logging.internal;
 
 import org.gradle.api.logging.LogLevel;
 
-/**
- * @author Hans Dockter
- */
 public interface LoggingConfigurer {
     void configure(LogLevel logLevel);
 }
diff --git a/subprojects/core/src/main/groovy/org/gradle/logging/internal/NoOpLoggingSystem.java b/subprojects/core/src/main/groovy/org/gradle/logging/internal/NoOpLoggingSystem.java
index 373cb99..983548d 100644
--- a/subprojects/core/src/main/groovy/org/gradle/logging/internal/NoOpLoggingSystem.java
+++ b/subprojects/core/src/main/groovy/org/gradle/logging/internal/NoOpLoggingSystem.java
@@ -18,9 +18,6 @@ package org.gradle.logging.internal;
 
 import org.gradle.api.logging.LogLevel;
 
-/**
- * by Szczepan Faber, created at: 11/21/11
- */
 public class NoOpLoggingSystem implements StdOutLoggingSystem, StdErrLoggingSystem, LoggingSystem {
     public Snapshot snapshot() {
         return dummy();
diff --git a/subprojects/core/src/main/groovy/org/gradle/logging/internal/StdErrLoggingSystem.java b/subprojects/core/src/main/groovy/org/gradle/logging/internal/StdErrLoggingSystem.java
index ed19c67..b7ede43 100644
--- a/subprojects/core/src/main/groovy/org/gradle/logging/internal/StdErrLoggingSystem.java
+++ b/subprojects/core/src/main/groovy/org/gradle/logging/internal/StdErrLoggingSystem.java
@@ -16,8 +16,5 @@
 
 package org.gradle.logging.internal;
 
-/**
- * by Szczepan Faber, created at: 11/21/11
- */
 public interface StdErrLoggingSystem extends LoggingSystem {
 }
diff --git a/subprojects/core/src/main/groovy/org/gradle/logging/internal/StdOutLoggingSystem.java b/subprojects/core/src/main/groovy/org/gradle/logging/internal/StdOutLoggingSystem.java
index 7592ea3..f4eecf4 100644
--- a/subprojects/core/src/main/groovy/org/gradle/logging/internal/StdOutLoggingSystem.java
+++ b/subprojects/core/src/main/groovy/org/gradle/logging/internal/StdOutLoggingSystem.java
@@ -16,8 +16,5 @@
 
 package org.gradle.logging.internal;
 
-/**
- * by Szczepan Faber, created at: 11/21/11
- */
 public interface StdOutLoggingSystem extends LoggingSystem {
 }
diff --git a/subprojects/core/src/main/groovy/org/gradle/logging/internal/logback/LogbackLoggingConfigurer.java b/subprojects/core/src/main/groovy/org/gradle/logging/internal/logback/LogbackLoggingConfigurer.java
index cea30a6..4eba18f 100644
--- a/subprojects/core/src/main/groovy/org/gradle/logging/internal/logback/LogbackLoggingConfigurer.java
+++ b/subprojects/core/src/main/groovy/org/gradle/logging/internal/logback/LogbackLoggingConfigurer.java
@@ -28,7 +28,9 @@ import ch.qos.logback.core.ConsoleAppender;
 import ch.qos.logback.core.spi.FilterReply;
 import org.gradle.api.logging.LogLevel;
 import org.gradle.internal.UncheckedException;
-import org.gradle.logging.internal.*;
+import org.gradle.logging.internal.LogEvent;
+import org.gradle.logging.internal.LoggingConfigurer;
+import org.gradle.logging.internal.OutputEventListener;
 import org.slf4j.LoggerFactory;
 import org.slf4j.Marker;
 
@@ -37,8 +39,6 @@ import java.io.PrintStream;
 /**
  * A {@link org.gradle.logging.internal.LoggingConfigurer} implementation which configures Logback
  * to route logging events to a {@link org.gradle.logging.internal.OutputEventListener}.
- *
- * @author Hans Dockter
  */
 public class LogbackLoggingConfigurer implements LoggingConfigurer {
     private final OutputEventListener outputEventListener;
diff --git a/subprojects/core/src/main/groovy/org/gradle/model/ModelType.java b/subprojects/core/src/main/groovy/org/gradle/model/ModelType.java
new file mode 100644
index 0000000..80ecbfc
--- /dev/null
+++ b/subprojects/core/src/main/groovy/org/gradle/model/ModelType.java
@@ -0,0 +1,28 @@
+/*
+ * Copyright 2013 the original author or authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.gradle.model;
+
+import java.lang.annotation.*;
+
+/**
+ * Marks a type as representing some type of model object.
+ */
+ at Inherited
+ at Target(ElementType.TYPE)
+ at Retention(RetentionPolicy.RUNTIME)
+public @interface ModelType {
+}
diff --git a/subprojects/core/src/main/groovy/org/gradle/model/internal/PersistentModelObjectRegistry.java b/subprojects/core/src/main/groovy/org/gradle/model/internal/PersistentModelObjectRegistry.java
new file mode 100644
index 0000000..09e2070
--- /dev/null
+++ b/subprojects/core/src/main/groovy/org/gradle/model/internal/PersistentModelObjectRegistry.java
@@ -0,0 +1,136 @@
+/*
+ * Copyright 2013 the original author or authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.gradle.model.internal;
+
+import com.google.common.collect.MapMaker;
+import org.gradle.api.GradleException;
+import org.gradle.api.Nullable;
+import org.gradle.cache.internal.btree.BTreePersistentIndexedCache;
+import org.gradle.internal.CompositeStoppable;
+import org.gradle.internal.reflect.JavaReflectionUtil;
+import org.gradle.internal.reflect.PropertyAccessor;
+import org.gradle.messaging.serialize.DefaultSerializer;
+import org.gradle.model.ModelType;
+
+import java.io.File;
+import java.io.Serializable;
+import java.util.Map;
+import java.util.TreeMap;
+
+public class PersistentModelObjectRegistry {
+    private final BTreePersistentIndexedCache<Object, FlattenedObject> store;
+    private final Map<Object, Object> idToInstance;
+    private final Map<Object, Object> instanceToId;
+
+    public PersistentModelObjectRegistry(File outputFile) {
+        store = new BTreePersistentIndexedCache<Object, FlattenedObject>(outputFile, new DefaultSerializer<Object>(), new DefaultSerializer<FlattenedObject>());
+        idToInstance = new MapMaker().weakValues().makeMap();
+        instanceToId = new MapMaker().weakKeys().makeMap();
+    }
+
+    public void put(Object identifier, Object modelObject) {
+        if (modelObject.getClass().getAnnotation(ModelType.class) == null) {
+            throw new IllegalArgumentException(String.format("Cannot persist object of class %s, as this class is not marked @%s", modelObject.getClass().getSimpleName(), ModelType.class.getSimpleName()));
+        }
+
+        FlattenedObject flattened = new FlattenedObject();
+        Class<?> type = modelObject.getClass();
+        for (PropertyAccessor property : JavaReflectionUtil.readableProperties(type).values()) {
+            if (property.getName().equals("metaClass") || property.getName().equals("class")) {
+                continue;
+            }
+            Object value;
+            try {
+                value = property.getValue(modelObject);
+            } catch (Exception e) {
+                throw new GradleException(String.format("Could not get property %s for model %s (%s)", property.getName(), identifier, modelObject.getClass().getSimpleName()), e);
+            }
+            if (value != null && value.getClass().getAnnotation(ModelType.class) != null) {
+                Object valueId = instanceToId.get(value);
+                if (valueId == null) {
+                    throw new IllegalStateException(String.format("Model %s (%s) references an unknown model object of type %s.", identifier, modelObject.getClass().getSimpleName(), value.getClass().getSimpleName()));
+                }
+                value = new Reference(value.getClass().getName(), valueId);
+            }
+            flattened.properties.put(property.getName(), value);
+        }
+
+        idToInstance.put(identifier, modelObject);
+        instanceToId.put(modelObject, identifier);
+        store.put(identifier, flattened);
+    }
+
+    @Nullable
+    public <T> T get(Object identifier, Class<T> type) {
+        Object modelObject = idToInstance.get(identifier);
+        if (modelObject != null) {
+            return type.cast(modelObject);
+        }
+
+        FlattenedObject flattened = store.get(identifier);
+        if (flattened == null) {
+            return null;
+        }
+        try {
+            modelObject = type.newInstance();
+        } catch (Exception e) {
+            throw new GradleException(String.format("Could not create an instance of %s.", type.getSimpleName()), e);
+        }
+        for (Map.Entry<String, Object> entry : flattened.properties.entrySet()) {
+            Object value = entry.getValue();
+            if (value instanceof Reference) {
+                Reference reference = (Reference) value;
+                Class<?> referenceType;
+                try {
+                    referenceType = type.getClassLoader().loadClass(reference.type);
+                } catch (ClassNotFoundException e) {
+                    throw new GradleException(String.format("Could not locate type %s referenced by model %s (%s)", reference.type, identifier, type.getSimpleName()));
+                }
+                value = get(reference.identifier, referenceType);
+            }
+            try {
+                JavaReflectionUtil.writeableProperty(modelObject.getClass(), entry.getKey()).setValue(modelObject, value);
+            } catch (Exception e) {
+                throw new GradleException(String.format("Could not set property %s for model %s (%s)", entry.getKey(), identifier, type.getSimpleName()), e);
+            }
+        }
+
+        idToInstance.put(identifier, modelObject);
+        instanceToId.put(modelObject, identifier);
+        return type.cast(modelObject);
+    }
+
+    public void close() {
+        CompositeStoppable.stoppable(store).stop();
+    }
+
+    private static class Reference implements Serializable {
+        final String type;
+        final Object identifier;
+
+        private Reference(String type, Object identifier) {
+            this.type = type;
+            this.identifier = identifier;
+        }
+    }
+
+    private static class FlattenedObject implements Serializable {
+        Map<String, Object> properties = new TreeMap<String, Object>();
+    }
+
+}
+
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 734401a..85ecb0e 100644
--- a/subprojects/core/src/main/groovy/org/gradle/process/BaseExecSpec.java
+++ b/subprojects/core/src/main/groovy/org/gradle/process/BaseExecSpec.java
@@ -21,8 +21,6 @@ import java.util.List;
 
 /**
  * Specifies options for launching a child process.
- *
- * @author Hans Dockter
  */
 public interface BaseExecSpec extends ProcessForkOptions {
     /**
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 df3ef6a..0109805 100644
--- a/subprojects/core/src/main/groovy/org/gradle/process/ExecResult.java
+++ b/subprojects/core/src/main/groovy/org/gradle/process/ExecResult.java
@@ -20,8 +20,6 @@ import org.gradle.process.internal.ExecException;
 
 /**
  * Represents the result of running an external process.
- *
- * @author Hans Dockter
  */
 public interface ExecResult {
     /**
diff --git a/subprojects/core/src/main/groovy/org/gradle/process/ExecSpec.java b/subprojects/core/src/main/groovy/org/gradle/process/ExecSpec.java
index 83ca73a..7cb1a9e 100644
--- a/subprojects/core/src/main/groovy/org/gradle/process/ExecSpec.java
+++ b/subprojects/core/src/main/groovy/org/gradle/process/ExecSpec.java
@@ -19,8 +19,6 @@ import java.util.List;
 
 /**
  * Specified the options for executing some command.
- *
- * @author Hans Dockter
  */
 public interface ExecSpec extends BaseExecSpec {
     /**
diff --git a/subprojects/core/src/main/groovy/org/gradle/process/JavaExecSpec.java b/subprojects/core/src/main/groovy/org/gradle/process/JavaExecSpec.java
index 8c5222c..f92d5bd 100644
--- a/subprojects/core/src/main/groovy/org/gradle/process/JavaExecSpec.java
+++ b/subprojects/core/src/main/groovy/org/gradle/process/JavaExecSpec.java
@@ -21,8 +21,6 @@ import java.util.List;
 
 /**
  * Specifies the options for executing a Java application.
- *
- * @author Hans Dockter
  */
 public interface JavaExecSpec extends JavaForkOptions, BaseExecSpec {
     /**
diff --git a/subprojects/core/src/main/groovy/org/gradle/process/internal/AbstractExecHandleBuilder.java b/subprojects/core/src/main/groovy/org/gradle/process/internal/AbstractExecHandleBuilder.java
index 9672afa..f5064af 100644
--- a/subprojects/core/src/main/groovy/org/gradle/process/internal/AbstractExecHandleBuilder.java
+++ b/subprojects/core/src/main/groovy/org/gradle/process/internal/AbstractExecHandleBuilder.java
@@ -27,9 +27,6 @@ import java.io.OutputStream;
 import java.util.ArrayList;
 import java.util.List;
 
-/**
- * @author Hans Dockter
- */
 public abstract class AbstractExecHandleBuilder extends DefaultProcessForkOptions implements BaseExecSpec {
     private OutputStream standardOutput;
     private OutputStream errorOutput;
diff --git a/subprojects/core/src/main/groovy/org/gradle/process/internal/BadExitCodeException.java b/subprojects/core/src/main/groovy/org/gradle/process/internal/BadExitCodeException.java
index 2a2f17b..edbf5d1 100644
--- a/subprojects/core/src/main/groovy/org/gradle/process/internal/BadExitCodeException.java
+++ b/subprojects/core/src/main/groovy/org/gradle/process/internal/BadExitCodeException.java
@@ -15,9 +15,6 @@
  */
 package org.gradle.process.internal;
 
-/**
- * @author Tom Eyckmans
- */
 public class BadExitCodeException extends Exception {
     public BadExitCodeException(String message) {
         super(message);
diff --git a/subprojects/core/src/main/groovy/org/gradle/process/internal/DefaultExecAction.java b/subprojects/core/src/main/groovy/org/gradle/process/internal/DefaultExecAction.java
index 86ef6fc..7ea563c 100644
--- a/subprojects/core/src/main/groovy/org/gradle/process/internal/DefaultExecAction.java
+++ b/subprojects/core/src/main/groovy/org/gradle/process/internal/DefaultExecAction.java
@@ -20,9 +20,6 @@ import org.gradle.api.internal.file.FileResolver;
 import org.gradle.api.internal.file.IdentityFileResolver;
 import org.gradle.process.ExecResult;
 
-/**
- * @author Hans Dockter
- */
 public class DefaultExecAction extends ExecHandleBuilder implements ExecAction {
     public DefaultExecAction(FileResolver fileResolver) {
         super(fileResolver);
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 20a330f..33849a8 100755
--- a/subprojects/core/src/main/groovy/org/gradle/process/internal/DefaultExecHandle.java
+++ b/subprojects/core/src/main/groovy/org/gradle/process/internal/DefaultExecHandle.java
@@ -17,7 +17,6 @@
 package org.gradle.process.internal;
 
 import com.google.common.base.Joiner;
-
 import org.gradle.api.logging.Logger;
 import org.gradle.api.logging.Logging;
 import org.gradle.internal.concurrent.DefaultExecutorFactory;
@@ -52,8 +51,6 @@ import java.util.concurrent.locks.ReentrantLock;
  * <li>{@link #start()} allowed when state is INIT</li>
  * <li>{@link #abort()} allowed when state is STARTED or DETACHED</li>
  * </ul>
- *
- * @author Tom Eyckmans
  */
 public class DefaultExecHandle implements ExecHandle, ProcessSettings {
     private static final Logger LOGGER = Logging.getLogger(DefaultExecHandle.class);
@@ -200,7 +197,7 @@ public class DefaultExecHandle implements ExecHandle, ProcessSettings {
                 }
             }
             setState(newState);
-            execResult = new ExecResultImpl(exitValue, wrappedException);
+            execResult = new ExecResultImpl(exitValue, wrappedException, displayName);
             result = execResult;
         } finally {
             lock.unlock();
@@ -344,13 +341,15 @@ public class DefaultExecHandle implements ExecHandle, ProcessSettings {
         return timeoutMillis;
     }
 
-    private class ExecResultImpl implements ExecResult {
+    private static class ExecResultImpl implements ExecResult {
         private final int exitValue;
         private final ExecException failure;
+        private final String displayName;
 
-        public ExecResultImpl(int exitValue, ExecException failure) {
+        public ExecResultImpl(int exitValue, ExecException failure, String displayName) {
             this.exitValue = exitValue;
             this.failure = failure;
+            this.displayName = displayName;
         }
 
         public int getExitValue() {
diff --git a/subprojects/core/src/main/groovy/org/gradle/process/internal/DefaultJavaExecAction.java b/subprojects/core/src/main/groovy/org/gradle/process/internal/DefaultJavaExecAction.java
index 0dcae9a..1adcfd5 100644
--- a/subprojects/core/src/main/groovy/org/gradle/process/internal/DefaultJavaExecAction.java
+++ b/subprojects/core/src/main/groovy/org/gradle/process/internal/DefaultJavaExecAction.java
@@ -19,9 +19,6 @@ package org.gradle.process.internal;
 import org.gradle.api.internal.file.FileResolver;
 import org.gradle.process.ExecResult;
 
-/**
- * @author Hans Dockter
- */
 public class DefaultJavaExecAction extends JavaExecHandleBuilder implements JavaExecAction {
     public DefaultJavaExecAction(FileResolver fileResolver) {
         super(fileResolver);
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 b4c7c3c..230bfba 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
@@ -27,7 +27,7 @@ import org.gradle.process.internal.child.ApplicationClassesInIsolatedClassLoader
 import org.gradle.process.internal.child.ApplicationClassesInSystemClassLoaderWorkerFactory;
 import org.gradle.process.internal.child.EncodedStream;
 import org.gradle.process.internal.child.WorkerFactory;
-import org.gradle.util.ClasspathUtil;
+import org.gradle.internal.classloader.ClasspathUtil;
 import org.gradle.util.GUtil;
 import org.slf4j.Logger;
 import org.slf4j.LoggerFactory;
diff --git a/subprojects/core/src/main/groovy/org/gradle/process/internal/ExecAction.java b/subprojects/core/src/main/groovy/org/gradle/process/internal/ExecAction.java
index ca68e64..854ce4f 100644
--- a/subprojects/core/src/main/groovy/org/gradle/process/internal/ExecAction.java
+++ b/subprojects/core/src/main/groovy/org/gradle/process/internal/ExecAction.java
@@ -15,12 +15,11 @@
  */
 package org.gradle.process.internal;
 
+import org.gradle.api.NonExtensible;
 import org.gradle.process.ExecResult;
 import org.gradle.process.ExecSpec;
 
-/**
- * @author Hans Dockter
- */
+ at NonExtensible
 public interface ExecAction extends ExecSpec {
     ExecResult execute() throws ExecException;
 }
diff --git a/subprojects/core/src/main/groovy/org/gradle/process/internal/ExecException.java b/subprojects/core/src/main/groovy/org/gradle/process/internal/ExecException.java
index 6999d85..2e964de 100644
--- a/subprojects/core/src/main/groovy/org/gradle/process/internal/ExecException.java
+++ b/subprojects/core/src/main/groovy/org/gradle/process/internal/ExecException.java
@@ -18,9 +18,6 @@ package org.gradle.process.internal;
 
 import org.gradle.api.GradleException;
 
-/**
- * @author Hans Dockter
- */
 public class ExecException extends GradleException {
     public ExecException(String message) {
         super(message);
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 9a999a1..5c58856 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
@@ -22,9 +22,6 @@ import java.io.File;
 import java.util.List;
 import java.util.Map;
 
-/**
- * @author Tom Eyckmans
- */
 public interface ExecHandle {
 
     File getDirectory();
diff --git a/subprojects/core/src/main/groovy/org/gradle/process/internal/ExecHandleBuilder.java b/subprojects/core/src/main/groovy/org/gradle/process/internal/ExecHandleBuilder.java
index 175d150..9ad91a5 100644
--- a/subprojects/core/src/main/groovy/org/gradle/process/internal/ExecHandleBuilder.java
+++ b/subprojects/core/src/main/groovy/org/gradle/process/internal/ExecHandleBuilder.java
@@ -29,9 +29,6 @@ import java.util.ArrayList;
 import java.util.Arrays;
 import java.util.List;
 
-/**
- * @author Tom Eyckmans
- */
 public class ExecHandleBuilder extends AbstractExecHandleBuilder implements ExecSpec {
     private final List<Object> arguments = new ArrayList<Object>();
 
diff --git a/subprojects/core/src/main/groovy/org/gradle/process/internal/ExecHandleListener.java b/subprojects/core/src/main/groovy/org/gradle/process/internal/ExecHandleListener.java
index a3f8b58..6ef4a82 100644
--- a/subprojects/core/src/main/groovy/org/gradle/process/internal/ExecHandleListener.java
+++ b/subprojects/core/src/main/groovy/org/gradle/process/internal/ExecHandleListener.java
@@ -18,9 +18,6 @@ package org.gradle.process.internal;
 
 import org.gradle.process.ExecResult;
 
-/**
- * @author Tom Eyckmans
- */
 public interface ExecHandleListener {
     void executionStarted(ExecHandle execHandle);
 
diff --git a/subprojects/core/src/main/groovy/org/gradle/process/internal/ExecHandleRunner.java b/subprojects/core/src/main/groovy/org/gradle/process/internal/ExecHandleRunner.java
index 7b86a21..b86fc58 100755
--- a/subprojects/core/src/main/groovy/org/gradle/process/internal/ExecHandleRunner.java
+++ b/subprojects/core/src/main/groovy/org/gradle/process/internal/ExecHandleRunner.java
@@ -23,9 +23,6 @@ import org.gradle.process.internal.streams.StreamsHandler;
 import java.util.concurrent.locks.Lock;
 import java.util.concurrent.locks.ReentrantLock;
 
-/**
- * @author Tom Eyckmans
- */
 public class ExecHandleRunner implements Runnable {
     private static final Object START_LOCK = new Object();
     private static final Logger LOGGER = Logging.getLogger(ExecHandleRunner.class);
diff --git a/subprojects/core/src/main/groovy/org/gradle/process/internal/ExecHandleShutdownHookAction.java b/subprojects/core/src/main/groovy/org/gradle/process/internal/ExecHandleShutdownHookAction.java
index f2cbddf..48a54c6 100644
--- a/subprojects/core/src/main/groovy/org/gradle/process/internal/ExecHandleShutdownHookAction.java
+++ b/subprojects/core/src/main/groovy/org/gradle/process/internal/ExecHandleShutdownHookAction.java
@@ -20,8 +20,6 @@ import org.slf4j.LoggerFactory;
 
 /**
  * Terminates the external running 'sub' process when the Gradle process is being cancelled.
- *
- * @author Tom Eyckmans
  */
 public class ExecHandleShutdownHookAction implements Runnable {
 
diff --git a/subprojects/core/src/main/groovy/org/gradle/process/internal/ExecHandleState.java b/subprojects/core/src/main/groovy/org/gradle/process/internal/ExecHandleState.java
index 783c337..b3e227b 100644
--- a/subprojects/core/src/main/groovy/org/gradle/process/internal/ExecHandleState.java
+++ b/subprojects/core/src/main/groovy/org/gradle/process/internal/ExecHandleState.java
@@ -16,9 +16,6 @@
 
 package org.gradle.process.internal;
 
-/**
- * @author Tom Eyckmans
- */
 public enum ExecHandleState {
     INIT,
     STARTING,
diff --git a/subprojects/core/src/main/groovy/org/gradle/process/internal/JavaExecAction.java b/subprojects/core/src/main/groovy/org/gradle/process/internal/JavaExecAction.java
index 81f5b49..7d9e654 100644
--- a/subprojects/core/src/main/groovy/org/gradle/process/internal/JavaExecAction.java
+++ b/subprojects/core/src/main/groovy/org/gradle/process/internal/JavaExecAction.java
@@ -15,12 +15,11 @@
  */
 package org.gradle.process.internal;
 
+import org.gradle.api.NonExtensible;
 import org.gradle.process.ExecResult;
 import org.gradle.process.JavaExecSpec;
 
-/**
- * @author Hans Dockter
- */
+ at NonExtensible
 public interface JavaExecAction extends JavaExecSpec {
     ExecResult execute();
 }
diff --git a/subprojects/core/src/main/groovy/org/gradle/process/internal/ProcessBuilderFactory.java b/subprojects/core/src/main/groovy/org/gradle/process/internal/ProcessBuilderFactory.java
index 59e4aa4..0957409 100644
--- a/subprojects/core/src/main/groovy/org/gradle/process/internal/ProcessBuilderFactory.java
+++ b/subprojects/core/src/main/groovy/org/gradle/process/internal/ProcessBuilderFactory.java
@@ -22,8 +22,6 @@ import java.util.Map;
 
 /**
  * Creates a {@link java.lang.ProcessBuilder} based on a {@link ExecHandle}.
- *
- * @author Tom Eyckmans
  */
 public class ProcessBuilderFactory {
     public ProcessBuilder createProcessBuilder(ProcessSettings processSettings) {
diff --git a/subprojects/core/src/main/groovy/org/gradle/process/internal/ProcessParentingInitializer.java b/subprojects/core/src/main/groovy/org/gradle/process/internal/ProcessParentingInitializer.java
index aba64ee..86cc12b 100644
--- a/subprojects/core/src/main/groovy/org/gradle/process/internal/ProcessParentingInitializer.java
+++ b/subprojects/core/src/main/groovy/org/gradle/process/internal/ProcessParentingInitializer.java
@@ -26,7 +26,6 @@ import org.gradle.internal.os.OperatingSystem;
 /**
  * Initializes for a well behaved parent process.
  * <p>
- * by Szczepan Faber, created at: 3/2/12
  */
 public class ProcessParentingInitializer {
 
diff --git a/subprojects/core/src/main/groovy/org/gradle/process/internal/ProcessSettings.java b/subprojects/core/src/main/groovy/org/gradle/process/internal/ProcessSettings.java
index f571230..b6dbe5a 100644
--- a/subprojects/core/src/main/groovy/org/gradle/process/internal/ProcessSettings.java
+++ b/subprojects/core/src/main/groovy/org/gradle/process/internal/ProcessSettings.java
@@ -20,9 +20,6 @@ import java.io.File;
 import java.util.List;
 import java.util.Map;
 
-/**
- * by Szczepan Faber, created at: 4/20/12
- */
 public interface ProcessSettings {
 
     File getDirectory();
diff --git a/subprojects/core/src/main/groovy/org/gradle/process/internal/child/EncodedStream.java b/subprojects/core/src/main/groovy/org/gradle/process/internal/child/EncodedStream.java
index b516b8c..e7a3159 100644
--- a/subprojects/core/src/main/groovy/org/gradle/process/internal/child/EncodedStream.java
+++ b/subprojects/core/src/main/groovy/org/gradle/process/internal/child/EncodedStream.java
@@ -23,8 +23,6 @@ import java.io.OutputStream;
 /**
  * Provides Input/OutputStream implementations that are able to encode/decode using a simple algorithm (byte<->2 digit hex string(2 bytes)).
  * Useful when streams are interpreted a text streams as it happens on IBM java for standard input.
- * <p>
- * by Szczepan Faber, created at: 5/25/12
  */
 public abstract class EncodedStream {
     private final static char[] HEX_DIGIT = new char[] {
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 a482c8c..144fbe3 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
@@ -19,6 +19,10 @@ package org.gradle.process.internal.child;
 import org.gradle.api.Action;
 import org.gradle.api.logging.LogLevel;
 import org.gradle.internal.UncheckedException;
+import org.gradle.internal.classloader.CachingClassLoader;
+import org.gradle.internal.classloader.FilteringClassLoader;
+import org.gradle.internal.classloader.MultiParentClassLoader;
+import org.gradle.internal.classloader.MutableURLClassLoader;
 import org.gradle.internal.io.ClassLoaderObjectInputStream;
 import org.gradle.logging.LoggingManagerInternal;
 import org.gradle.logging.LoggingServiceRegistry;
@@ -86,6 +90,6 @@ public class ImplementationClassLoaderWorker implements Action<WorkerContext>, S
     }
 
     MutableURLClassLoader createImplementationClassLoader(ClassLoader system, ClassLoader application) {
-        return new MutableURLClassLoader(new MultiParentClassLoader(application, system));
+        return new MutableURLClassLoader(new CachingClassLoader(new MultiParentClassLoader(application, system)));
     }
 }
diff --git a/subprojects/core/src/main/groovy/org/gradle/process/internal/child/IsolatedApplicationClassLoaderWorker.java b/subprojects/core/src/main/groovy/org/gradle/process/internal/child/IsolatedApplicationClassLoaderWorker.java
index a069bb7..b8511c9 100644
--- a/subprojects/core/src/main/groovy/org/gradle/process/internal/child/IsolatedApplicationClassLoaderWorker.java
+++ b/subprojects/core/src/main/groovy/org/gradle/process/internal/child/IsolatedApplicationClassLoaderWorker.java
@@ -17,7 +17,7 @@
 package org.gradle.process.internal.child;
 
 import org.gradle.api.Action;
-import org.gradle.util.DefaultClassLoaderFactory;
+import org.gradle.internal.classloader.DefaultClassLoaderFactory;
 
 import java.io.Serializable;
 import java.net.URI;
diff --git a/subprojects/core/src/main/groovy/org/gradle/process/internal/child/SystemApplicationClassLoaderWorker.java b/subprojects/core/src/main/groovy/org/gradle/process/internal/child/SystemApplicationClassLoaderWorker.java
index ae6448f..dec4ff6 100644
--- a/subprojects/core/src/main/groovy/org/gradle/process/internal/child/SystemApplicationClassLoaderWorker.java
+++ b/subprojects/core/src/main/groovy/org/gradle/process/internal/child/SystemApplicationClassLoaderWorker.java
@@ -25,7 +25,6 @@ import java.util.concurrent.Callable;
 /**
  * <p>Stage 3 of the start-up for a worker process with the application classes loaded in the system ClassLoader. Takes
  * care of adding the application classes to the system ClassLoader and then invoking the next stage of start-up.</p>
- * TODO:DAZ No longer adds application classes to system ClassLoader: is this required at all?
  *
  * <p> Instantiated in the worker bootstrap ClassLoader and invoked from {@link org.gradle.process.internal.launcher.BootstrapClassLoaderWorker}.
  * See {@link ApplicationClassesInSystemClassLoaderWorkerFactory} for details.</p>
diff --git a/subprojects/core/src/main/groovy/org/gradle/process/internal/child/WorkerProcessClassPathProvider.java b/subprojects/core/src/main/groovy/org/gradle/process/internal/child/WorkerProcessClassPathProvider.java
index 4cfe4e9..56ed0a1 100644
--- a/subprojects/core/src/main/groovy/org/gradle/process/internal/child/WorkerProcessClassPathProvider.java
+++ b/subprojects/core/src/main/groovy/org/gradle/process/internal/child/WorkerProcessClassPathProvider.java
@@ -65,6 +65,7 @@ public class WorkerProcessClassPathProvider implements ClassPathProvider {
             classpath = classpath.plus(moduleRegistry.getExternalModule("logback-classic").getClasspath());
             classpath = classpath.plus(moduleRegistry.getExternalModule("logback-core").getClasspath());
             classpath = classpath.plus(moduleRegistry.getExternalModule("jul-to-slf4j").getClasspath());
+            classpath = classpath.plus(moduleRegistry.getExternalModule("guava").getClasspath());
             return classpath;
         }
         if (name.equals("WORKER_MAIN")) {
diff --git a/subprojects/core/src/main/groovy/org/gradle/process/internal/shutdown/ShutdownHookActionRegister.java b/subprojects/core/src/main/groovy/org/gradle/process/internal/shutdown/ShutdownHookActionRegister.java
index 24eca1f..bb18e2b 100644
--- a/subprojects/core/src/main/groovy/org/gradle/process/internal/shutdown/ShutdownHookActionRegister.java
+++ b/subprojects/core/src/main/groovy/org/gradle/process/internal/shutdown/ShutdownHookActionRegister.java
@@ -22,9 +22,6 @@ import java.io.IOException;
 import java.util.List;
 import java.util.concurrent.CopyOnWriteArrayList;
 
-/**
- * @author Tom Eyckmans
- */
 public class ShutdownHookActionRegister {
     private static final ShutdownHookActionRegister INSTANCE = new ShutdownHookActionRegister();
     private final List<Runnable> shutdownHookActions = new CopyOnWriteArrayList<Runnable>();
diff --git a/subprojects/core/src/main/groovy/org/gradle/process/internal/streams/ExecOutputHandleRunner.java b/subprojects/core/src/main/groovy/org/gradle/process/internal/streams/ExecOutputHandleRunner.java
index e1fb0ec..9f4e432 100644
--- a/subprojects/core/src/main/groovy/org/gradle/process/internal/streams/ExecOutputHandleRunner.java
+++ b/subprojects/core/src/main/groovy/org/gradle/process/internal/streams/ExecOutputHandleRunner.java
@@ -25,9 +25,6 @@ import java.io.InputStream;
 import java.io.OutputStream;
 import java.util.concurrent.Executor;
 
-/**
- * @author Tom Eyckmans
- */
 public class ExecOutputHandleRunner implements Runnable {
     private final static Logger LOGGER = Logging.getLogger(ExecOutputHandleRunner.class);
 
diff --git a/subprojects/core/src/main/groovy/org/gradle/process/internal/streams/SafeStreams.java b/subprojects/core/src/main/groovy/org/gradle/process/internal/streams/SafeStreams.java
index d751b39..990e4f8 100644
--- a/subprojects/core/src/main/groovy/org/gradle/process/internal/streams/SafeStreams.java
+++ b/subprojects/core/src/main/groovy/org/gradle/process/internal/streams/SafeStreams.java
@@ -22,9 +22,6 @@ import java.io.ByteArrayInputStream;
 import java.io.InputStream;
 import java.io.OutputStream;
 
-/**
- * by Szczepan Faber, created at: 4/17/12
- */
 public class SafeStreams {
 
     public static OutputStream systemErr() {
diff --git a/subprojects/core/src/main/groovy/org/gradle/process/internal/streams/StreamsForwarder.java b/subprojects/core/src/main/groovy/org/gradle/process/internal/streams/StreamsForwarder.java
index 14fd1b2..35efc00 100644
--- a/subprojects/core/src/main/groovy/org/gradle/process/internal/streams/StreamsForwarder.java
+++ b/subprojects/core/src/main/groovy/org/gradle/process/internal/streams/StreamsForwarder.java
@@ -24,9 +24,6 @@ import java.io.IOException;
 import java.io.InputStream;
 import java.io.OutputStream;
 
-/**
- * by Szczepan Faber, created at: 4/17/12
- */
 public class StreamsForwarder implements StreamsHandler {
 
     private final OutputStream standardOutput;
@@ -47,6 +44,11 @@ public class StreamsForwarder implements StreamsHandler {
     }
 
     public void connectStreams(Process process, String processName) {
+        /*
+            There's a potential problem here in that DisconnectableInputStream reads from input in the background.
+            This won't automatically stop when the process is over. Therefore, if input is not closed then this thread
+            will run forever. It would be better to ensure that this thread stops when the process does.
+         */
         InputStream instr = new DisconnectableInputStream(input);
 
         standardOutputRunner = new ExecOutputHandleRunner("read standard output of: " + processName,
diff --git a/subprojects/core/src/main/groovy/org/gradle/process/internal/streams/StreamsHandler.java b/subprojects/core/src/main/groovy/org/gradle/process/internal/streams/StreamsHandler.java
index f13d605..40e9c83 100644
--- a/subprojects/core/src/main/groovy/org/gradle/process/internal/streams/StreamsHandler.java
+++ b/subprojects/core/src/main/groovy/org/gradle/process/internal/streams/StreamsHandler.java
@@ -18,9 +18,6 @@ package org.gradle.process.internal.streams;
 
 import org.gradle.internal.Stoppable;
 
-/**
- * by Szczepan Faber, created at: 4/27/12
- */
 public interface StreamsHandler extends Stoppable {
 
     void start();
diff --git a/subprojects/core/src/main/groovy/org/gradle/profile/BuildProfile.java b/subprojects/core/src/main/groovy/org/gradle/profile/BuildProfile.java
index d574371..7727436 100644
--- a/subprojects/core/src/main/groovy/org/gradle/profile/BuildProfile.java
+++ b/subprojects/core/src/main/groovy/org/gradle/profile/BuildProfile.java
@@ -15,10 +15,11 @@
  */
 package org.gradle.profile;
 
-import org.gradle.api.Project;
-import org.gradle.api.artifacts.ResolvableDependencies;
-import org.gradle.api.invocation.Gradle;
+import org.gradle.StartParameter;
+import org.gradle.util.CollectionUtils;
 
+import java.text.DateFormat;
+import java.text.SimpleDateFormat;
 import java.util.ArrayList;
 import java.util.LinkedHashMap;
 import java.util.List;
@@ -35,28 +36,26 @@ import java.util.Map;
  * <li>setBuildStarted</li>
  * <li>setSettingsEvaluated</li>
  * <li>setProjectsLoaded</li>
- * <li>setProjectsEvaluated</li>
+ * <li>setProjectsConfigured</li>
  * <li>setBuildFinished</li>
  * </ul>
  */
 public class BuildProfile {
-    private final Gradle gradle;
-    private final Map<Project, ProjectProfile> projects = new LinkedHashMap<Project, ProjectProfile>();
-    private final Map<String, DependencyResolveProfile> dependencySets = new LinkedHashMap<String, DependencyResolveProfile>();
+
+    private static final DateFormat DATE_FORMAT = new SimpleDateFormat("yyyy/MM/dd - HH:mm:ss");
+
+    private final Map<String, ProjectProfile> projects = new LinkedHashMap<String, ProjectProfile>();
+    private final Map<String, ContinuousOperation> dependencySets = new LinkedHashMap<String, ContinuousOperation>();
     private long profilingStarted;
     private long buildStarted;
     private long settingsEvaluated;
     private long projectsLoaded;
-    private long projectsEvaluated;
     private long buildFinished;
+    private StartParameter startParameter;
     private boolean successful;
 
-    public BuildProfile(Gradle gradle) {
-        this.gradle = gradle;
-    }
-
-    public Gradle getGradle() {
-        return gradle;
+    public BuildProfile(StartParameter startParameter) {
+        this.startParameter = startParameter;
     }
 
     public long getBuildStarted() {
@@ -64,33 +63,43 @@ public class BuildProfile {
     }
 
     /**
-     * Get a description of the tasks passed to gradle as targets from the command line
-     * @return
+     * Get a description of this profiled build. It contains info about tasks passed to gradle as targets from the command line.
      */
-    public String getTaskDescription() {
-        StringBuilder result = new StringBuilder();
-        for (String name : gradle.getStartParameter().getExcludedTaskNames()) {
-            result.append("-x");
-            result.append(name);
-            result.append(" ");
+    public String getBuildDescription() {
+        StringBuilder sb = new StringBuilder();
+        for (String name : startParameter.getExcludedTaskNames()) {
+            sb.append("-x ");
+            sb.append(name);
+            sb.append(" ");
         }
-        for (String name : gradle.getStartParameter().getTaskNames()) {
-            result.append(name);
-            result.append(" ");
+        for (String name : startParameter.getTaskNames()) {
+            sb.append(name);
+            sb.append(" ");
         }
-        return result.toString();
+        String tasks = sb.toString();
+        if (tasks.length() == 0) {
+            tasks = "(no tasks specified)";
+        }
+        return String.format("Profiled build: %s", tasks);
+    }
+
+    public boolean isSuccessful() {
+        return successful;
+    }
+
+    public void setSuccessful(boolean successful) {
+        this.successful = successful;
     }
 
     /**
      * Get the profiling container for the specified project
-     * @param project to look up
-     * @return
+     * @param projectPath to look up
      */
-    public ProjectProfile getProjectProfile(Project project) {
-        ProjectProfile result = projects.get(project);
+    public ProjectProfile getProjectProfile(String projectPath) {
+        ProjectProfile result = projects.get(projectPath);
         if (result == null) {
-            result = new ProjectProfile(project);
-            projects.put(project, result);
+            result = new ProjectProfile(projectPath);
+            projects.put(projectPath, result);
         }
         return result;
     }
@@ -100,28 +109,30 @@ public class BuildProfile {
      * @return list
      */
     public List<ProjectProfile> getProjects() {
-        return new ArrayList<ProjectProfile>(projects.values());
+        return CollectionUtils.sort(projects.values(), Operation.slowestFirst());
     }
 
     public CompositeOperation<Operation> getProjectConfiguration() {
         List<Operation> operations = new ArrayList<Operation>();
         for (ProjectProfile projectProfile : projects.values()) {
-            operations.add(projectProfile.getEvaluation());
+            operations.add(projectProfile.getConfigurationOperation());
         }
+        operations = CollectionUtils.sort(operations, Operation.slowestFirst());
         return new CompositeOperation<Operation>(operations);
     }
 
-    public DependencyResolveProfile getDependencySetProfile(ResolvableDependencies dependencySet) {
-        DependencyResolveProfile profile = dependencySets.get(dependencySet.getPath());
+    public ContinuousOperation getDependencySetProfile(String dependencySetDescription) {
+        ContinuousOperation profile = dependencySets.get(dependencySetDescription);
         if (profile == null) {
-            profile = new DependencyResolveProfile(dependencySet);
-            dependencySets.put(dependencySet.getPath(), profile);
+            profile = new ContinuousOperation(dependencySetDescription);
+            dependencySets.put(dependencySetDescription, profile);
         }
         return profile;
     }
 
-    public CompositeOperation<DependencyResolveProfile> getDependencySets() {
-        return new CompositeOperation<DependencyResolveProfile>(dependencySets.values());
+    public CompositeOperation<ContinuousOperation> getDependencySets() {
+        final List<ContinuousOperation> profiles = CollectionUtils.sort(dependencySets.values(), Operation.slowestFirst());
+        return new CompositeOperation<ContinuousOperation>(profiles);
     }
 
     /**
@@ -160,15 +171,6 @@ public class BuildProfile {
     }
 
     /**
-     * Should be set with a timestamp from a {@link org.gradle.BuildListener#projectsEvaluated}
-     * callback.
-     * @param projectsEvaluated
-     */
-    public void setProjectsEvaluated(long projectsEvaluated) {
-        this.projectsEvaluated = projectsEvaluated;
-    }
-
-    /**
      * Should be set with a timestamp from a {@link org.gradle.BuildListener#buildFinished}
      * callback.
      * @param buildFinished
@@ -211,38 +213,22 @@ public class BuildProfile {
     }
 
     /**
-     * Get the elapsed time (in mSec) between the projectsEvaluated event and the projectsLoaded event.
-     * @return
-     */
-    public long getElapsedProjectsEvaluated() {
-        return projectsEvaluated - projectsLoaded;
-    }
-
-    /**
-     * Get the elapsed time (in mSec) between the buildFinished event and the projectsEvaluated event.
-     * @return
-     */
-    public long getElapsedAfterProjectsEvaluated() {
-        return buildFinished - projectsEvaluated;
-    }
-
-    /**
      * Get the total task execution time from all projects.
      * @return
      */
     public long getElapsedTotalExecutionTime() {
         long result = 0;
         for (ProjectProfile projectProfile : projects.values()) {
-            result += projectProfile.getTasks().getElapsedTime();
+            result += projectProfile.getElapsedTime();
         }
         return result;
     }
 
-    public boolean isSuccessful() {
-        return successful;
+    public String getBuildStartedDescription() {
+        return String.format("Started on: %s", DATE_FORMAT.format(buildStarted));
     }
 
-    public void setSuccessful(boolean successful) {
-        this.successful = successful;
+    public StartParameter getStartParameter() {
+        return startParameter;
     }
 }
diff --git a/subprojects/core/src/main/groovy/org/gradle/profile/CompositeOperation.java b/subprojects/core/src/main/groovy/org/gradle/profile/CompositeOperation.java
index 6965bd4..4fe6b75 100644
--- a/subprojects/core/src/main/groovy/org/gradle/profile/CompositeOperation.java
+++ b/subprojects/core/src/main/groovy/org/gradle/profile/CompositeOperation.java
@@ -47,4 +47,8 @@ public class CompositeOperation<T extends Operation> extends Operation implement
         }
         return sum;
     }
+
+    public String getDescription() {
+        return "<composite operation>";
+    }
 }
diff --git a/subprojects/core/src/main/groovy/org/gradle/profile/ContinuousOperation.java b/subprojects/core/src/main/groovy/org/gradle/profile/ContinuousOperation.java
index c0b342c..2e647e1 100644
--- a/subprojects/core/src/main/groovy/org/gradle/profile/ContinuousOperation.java
+++ b/subprojects/core/src/main/groovy/org/gradle/profile/ContinuousOperation.java
@@ -21,13 +21,25 @@ package org.gradle.profile;
 public class ContinuousOperation extends Operation {
     private long start;
     private long finish;
+    private String description;
 
-    public void setStart(long start) {
+    public ContinuousOperation(String description) {
+        this.description = description;
+    }
+
+    @Override
+    public String toString() {
+        return description;
+    }
+
+    public ContinuousOperation setStart(long start) {
         this.start = start;
+        return this;
     }
 
-    public void setFinish(long finish) {
+    public ContinuousOperation setFinish(long finish) {
         this.finish = finish;
+        return this;
     }
 
     public long getStartTime() {
@@ -37,4 +49,8 @@ public class ContinuousOperation extends Operation {
     public long getElapsedTime() {
         return finish - start;
     }
+
+    public String getDescription() {
+        return description;
+    }
 }
diff --git a/subprojects/core/src/main/groovy/org/gradle/profile/DependencyResolveProfile.java b/subprojects/core/src/main/groovy/org/gradle/profile/DependencyResolveProfile.java
deleted file mode 100644
index 8c703a5..0000000
--- a/subprojects/core/src/main/groovy/org/gradle/profile/DependencyResolveProfile.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.profile;
-
-import org.gradle.api.artifacts.ResolvableDependencies;
-
-public class DependencyResolveProfile extends ContinuousOperation {
-    private final ResolvableDependencies dependencySet;
-
-    public DependencyResolveProfile(ResolvableDependencies dependencySet) {
-        this.dependencySet = dependencySet;
-    }
-
-    public String getPath() {
-        return dependencySet.getPath();
-    }
-}
diff --git a/subprojects/core/src/main/groovy/org/gradle/profile/EvalutationOperation.java b/subprojects/core/src/main/groovy/org/gradle/profile/EvalutationOperation.java
deleted file mode 100644
index b886e07..0000000
--- a/subprojects/core/src/main/groovy/org/gradle/profile/EvalutationOperation.java
+++ /dev/null
@@ -1,31 +0,0 @@
-/*
- * Copyright 2013 the original author or authors.
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- *      http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package org.gradle.profile;
-
-import org.gradle.api.Project;
-
-public class EvalutationOperation extends ContinuousOperation {
-    private final Project project;
-
-    public EvalutationOperation(Project project) {
-        this.project = project;
-    }
-
-    public String getPath(){
-        return project.getPath();
-    }
-}
diff --git a/subprojects/core/src/main/groovy/org/gradle/profile/Operation.java b/subprojects/core/src/main/groovy/org/gradle/profile/Operation.java
index 591b71e..453b924 100644
--- a/subprojects/core/src/main/groovy/org/gradle/profile/Operation.java
+++ b/subprojects/core/src/main/groovy/org/gradle/profile/Operation.java
@@ -15,6 +15,8 @@
  */
 package org.gradle.profile;
 
+import java.util.Comparator;
+
 /**
  * A general operation.
  */
@@ -23,4 +25,23 @@ public abstract class Operation {
      * Returns the total elapsed execution time of this operation in millis.
      */
     abstract long getElapsedTime();
+
+    abstract String getDescription();
+
+    /**
+     * @return comparator that compares operations, slowest first, then alphabetically
+     */
+    public static Comparator<? super Operation> slowestFirst() {
+        return new Comparator<Operation>() {
+            public int compare(Operation o1, Operation o2) {
+                long byElapsedTime = o2.getElapsedTime() - o1.getElapsedTime();
+                if (byElapsedTime > 0) {
+                    return 1;
+                } else if (byElapsedTime < 0) {
+                    return -1;
+                }
+                return o1.getDescription().compareTo(o2.getDescription());
+            }
+        };
+    }
 }
diff --git a/subprojects/core/src/main/groovy/org/gradle/profile/ProfileEventAdapter.java b/subprojects/core/src/main/groovy/org/gradle/profile/ProfileEventAdapter.java
index 81228bd..f28693c 100644
--- a/subprojects/core/src/main/groovy/org/gradle/profile/ProfileEventAdapter.java
+++ b/subprojects/core/src/main/groovy/org/gradle/profile/ProfileEventAdapter.java
@@ -47,8 +47,9 @@ public class ProfileEventAdapter implements BuildListener, ProjectEvaluationList
 
     // BuildListener
     public void buildStarted(Gradle gradle) {
-        buildProfile = new BuildProfile(gradle);
-        buildProfile.setBuildStarted(timeProvider.getCurrentTime());
+        long now = timeProvider.getCurrentTime();
+        buildProfile = new BuildProfile(gradle.getStartParameter());
+        buildProfile.setBuildStarted(now);
         buildProfile.setProfilingStarted(buildMetaData.getBuildTimeClock().getStartTime());
     }
 
@@ -60,9 +61,7 @@ public class ProfileEventAdapter implements BuildListener, ProjectEvaluationList
         buildProfile.setProjectsLoaded(timeProvider.getCurrentTime());
     }
 
-    public void projectsEvaluated(Gradle gradle) {
-        buildProfile.setProjectsEvaluated(timeProvider.getCurrentTime());
-    }
+    public void projectsEvaluated(Gradle gradle) {}
 
     public void buildFinished(BuildResult result) {
         buildProfile.setBuildFinished(timeProvider.getCurrentTime());
@@ -76,39 +75,42 @@ public class ProfileEventAdapter implements BuildListener, ProjectEvaluationList
 
     // ProjectEvaluationListener
     public void beforeEvaluate(Project project) {
-        buildProfile.getProjectProfile(project).getEvaluation().setStart(System.currentTimeMillis());
+        long now = timeProvider.getCurrentTime();
+        buildProfile.getProjectProfile(project.getPath()).getConfigurationOperation().setStart(now);
     }
 
     public void afterEvaluate(Project project, ProjectState state) {
-        ProjectProfile projectProfile = buildProfile.getProjectProfile(project);
-        projectProfile.getEvaluation().setFinish(timeProvider.getCurrentTime());
-        projectProfile.setState(state);
+        long now = timeProvider.getCurrentTime();
+        ProjectProfile projectProfile = buildProfile.getProjectProfile(project.getPath());
+        projectProfile.getConfigurationOperation().setFinish(now);
     }
 
     // TaskExecutionListener
     public void beforeExecute(Task task) {
+        long now = timeProvider.getCurrentTime();
         Project project = task.getProject();
-        ProjectProfile projectProfile = buildProfile.getProjectProfile(project);
-        projectProfile.getTaskProfile(task).setStart(timeProvider.getCurrentTime());
+        ProjectProfile projectProfile = buildProfile.getProjectProfile(project.getPath());
+        projectProfile.getTaskProfile(task.getPath()).setStart(now);
     }
 
     public void afterExecute(Task task, TaskState state) {
+        long now = timeProvider.getCurrentTime();
         Project project = task.getProject();
-        ProjectProfile projectProfile = buildProfile.getProjectProfile(project);
-        TaskExecution taskExecution = projectProfile.getTaskProfile(task);
-        taskExecution.setFinish(timeProvider.getCurrentTime());
-        taskExecution.setState(state);
+        ProjectProfile projectProfile = buildProfile.getProjectProfile(project.getPath());
+        TaskExecution taskExecution = projectProfile.getTaskProfile(task.getPath());
+        taskExecution.setFinish(now);
+        taskExecution.completed(state);
     }
 
     // DependencyResolutionListener
     public void beforeResolve(ResolvableDependencies dependencies) {
-        DependencyResolveProfile profile = buildProfile.getDependencySetProfile(dependencies);
-        profile.setStart(timeProvider.getCurrentTime());
+        long now = timeProvider.getCurrentTime();
+        buildProfile.getDependencySetProfile(dependencies.getPath()).setStart(now);
     }
 
     public void afterResolve(ResolvableDependencies dependencies) {
-        DependencyResolveProfile profile = buildProfile.getDependencySetProfile(dependencies);
-        profile.setFinish(timeProvider.getCurrentTime());
+        long now = timeProvider.getCurrentTime();
+        buildProfile.getDependencySetProfile(dependencies.getPath()).setFinish(now);
     }
 }
 
diff --git a/subprojects/core/src/main/groovy/org/gradle/profile/ProfileReportRenderer.java b/subprojects/core/src/main/groovy/org/gradle/profile/ProfileReportRenderer.java
index 9f5f6e4..3db502b 100644
--- a/subprojects/core/src/main/groovy/org/gradle/profile/ProfileReportRenderer.java
+++ b/subprojects/core/src/main/groovy/org/gradle/profile/ProfileReportRenderer.java
@@ -20,16 +20,10 @@ import org.gradle.reporting.DurationFormatter;
 import org.gradle.reporting.HtmlReportRenderer;
 import org.gradle.reporting.ReportRenderer;
 import org.gradle.reporting.TabbedPageRenderer;
-import org.gradle.util.CollectionUtils;
 
 import java.io.File;
 import java.io.IOException;
-import java.text.DateFormat;
-import java.text.SimpleDateFormat;
-import java.util.Comparator;
-import java.util.List;
 
-//TODO SF add coverage
 public class ProfileReportRenderer {
     public void writeTo(BuildProfile buildProfile, File file) {
         HtmlReportRenderer renderer = new HtmlReportRenderer();
@@ -42,8 +36,6 @@ public class ProfileReportRenderer {
     private static final DurationFormatter DURATION_FORMAT = new DurationFormatter();
 
     private static class ProfilePageRenderer extends TabbedPageRenderer<BuildProfile> {
-        static final DateFormat DATE_FORMAT = new SimpleDateFormat("yyyy/MM/dd - HH:mm:ss");
-
         @Override
         protected String getTitle() {
             return "Profile report";
@@ -55,8 +47,8 @@ public class ProfileReportRenderer {
                 @Override
                 public void render(BuildProfile model, SimpleHtmlWriter htmlWriter) throws IOException {
                     htmlWriter.startElement("div").attribute("id", "header")
-                        .startElement("p").characters(String.format("Profiled with tasks: %s", model.getTaskDescription())).endElement()
-                        .startElement("p").characters(String.format("Run on: %s", DATE_FORMAT.format(model.getBuildStarted()))).endElement()
+                        .startElement("p").characters(model.getBuildDescription()).endElement()
+                        .startElement("p").characters(model.getBuildStartedDescription()).endElement()
                     .endElement();
                 }
             };
@@ -67,6 +59,8 @@ public class ProfileReportRenderer {
             return new ReportRenderer<BuildProfile, SimpleHtmlWriter>() {
                 @Override
                 public void render(BuildProfile model, SimpleHtmlWriter htmlWriter) throws IOException {
+                    CompositeOperation<Operation> profiledProjectConfiguration = model.getProjectConfiguration();
+
                     htmlWriter.startElement("div").attribute("id", "tabs")
                         .startElement("ul").attribute("class", "tabLinks")
                             .startElement("li").startElement("a").attribute("href", "#tab0").characters("Summary").endElement().endElement()
@@ -101,7 +95,7 @@ public class ProfileReportRenderer {
                                 htmlWriter.endElement();
                                 htmlWriter.startElement("tr");
                                     htmlWriter.startElement("td").characters("Configuring Projects").endElement();
-                                    htmlWriter.startElement("td").attribute("class", "numeric").characters(DURATION_FORMAT.format(model.getElapsedAfterProjectsEvaluated())).endElement();
+                                    htmlWriter.startElement("td").attribute("class", "numeric").characters(DURATION_FORMAT.format(profiledProjectConfiguration.getElapsedTime())).endElement();
                                 htmlWriter.endElement();
                                 htmlWriter.startElement("tr");
                                     htmlWriter.startElement("td").characters("Task Execution").endElement();
@@ -120,18 +114,12 @@ public class ProfileReportRenderer {
                                 htmlWriter.endElement();
                                 htmlWriter.startElement("tr");
                                     htmlWriter.startElement("td").characters("All projects").endElement();
-                                    htmlWriter.startElement("td").attribute("class", "numeric").characters(DURATION_FORMAT.format(model.getProjectConfiguration().getElapsedTime())).endElement();
+                                    htmlWriter.startElement("td").attribute("class", "numeric").characters(DURATION_FORMAT.format(profiledProjectConfiguration.getElapsedTime())).endElement();
                                 htmlWriter.endElement();
-                                final List<Operation> operations = CollectionUtils.sort(model.getProjectConfiguration().getOperations(), new Comparator<Operation>() {
-                                    public int compare(Operation o1, Operation o2) {
-                                        return Long.valueOf(o2.getElapsedTime()).compareTo(Long.valueOf(o1.getElapsedTime()));
-                                    }
-                                });
-                                for (Operation operation : operations) {
-                                    EvalutationOperation evalOperation = (EvalutationOperation)operation;
+                                for (Operation operation : profiledProjectConfiguration) {
                                     htmlWriter.startElement("tr");
-                                        htmlWriter.startElement("td").characters(evalOperation.getPath()).endElement();
-                                        htmlWriter.startElement("td").attribute("class", "numeric").characters(DURATION_FORMAT.format(evalOperation.getElapsedTime())).endElement();
+                                        htmlWriter.startElement("td").characters(operation.getDescription()).endElement();
+                                        htmlWriter.startElement("td").attribute("class", "numeric").characters(DURATION_FORMAT.format(operation.getElapsedTime())).endElement();
                                     htmlWriter.endElement();
                                 }
                             htmlWriter.endElement()
@@ -150,15 +138,10 @@ public class ProfileReportRenderer {
                                     htmlWriter.startElement("td").attribute("class", "numeric").characters(DURATION_FORMAT.format(model.getDependencySets().getElapsedTime())).endElement();
                                 htmlWriter.endElement();
 
-                                final List<DependencyResolveProfile> dependencyResolveProfiles = CollectionUtils.sort(model.getDependencySets().getOperations(), new Comparator<DependencyResolveProfile>() {
-                                        public int compare(DependencyResolveProfile p1, DependencyResolveProfile p2) {
-                                        return Long.valueOf(p2.getElapsedTime()).compareTo(Long.valueOf(p1.getElapsedTime()));
-                                    }
-                                    });
-                                for (DependencyResolveProfile profile : dependencyResolveProfiles) {
+                                for (Operation operation : model.getDependencySets()) {
                                     htmlWriter.startElement("tr");
-                                        htmlWriter.startElement("td").characters(profile.getPath()).endElement();
-                                        htmlWriter.startElement("td").attribute("class", "numeric").characters(DURATION_FORMAT.format(profile.getElapsedTime())).endElement();
+                                        htmlWriter.startElement("td").characters(operation.getDescription()).endElement();
+                                        htmlWriter.startElement("td").attribute("class", "numeric").characters(DURATION_FORMAT.format(operation.getElapsedTime())).endElement();
                                     htmlWriter.endElement();
                                 }
                             htmlWriter.endElement()
@@ -173,27 +156,17 @@ public class ProfileReportRenderer {
                                         .startElement("th").characters("Result").endElement()
                                     .endElement()
                                 .endElement();
-                                final List<ProjectProfile> projects = CollectionUtils.sort(model.getProjects(), new Comparator<ProjectProfile>() {
-                                        public int compare(ProjectProfile p1, ProjectProfile p2) {
-                                        return Long.valueOf(p2.getTasks().getElapsedTime()).compareTo(p1.getTasks().getElapsedTime());
-                                    }
-                                });
-                                for (ProjectProfile project : projects) {
+                                for (ProjectProfile project : model.getProjects()) {
                                    htmlWriter.startElement("tr")
                                         .startElement("td").characters(project.getPath()).endElement()
-                                        .startElement("td").attribute("class", "numeric").characters(DURATION_FORMAT.format(project.getTasks().getElapsedTime())).endElement()
+                                        .startElement("td").attribute("class", "numeric").characters(DURATION_FORMAT.format(project.getElapsedTime())).endElement()
                                         .startElement("td").characters("(total)").endElement()
                                     .endElement();
-                                    final List<TaskExecution> taskExecutions = CollectionUtils.sort(project.getTasks().getOperations(), new Comparator<TaskExecution>() {
-                                        public int compare(TaskExecution p1, TaskExecution p2) {
-                                            return Long.valueOf(p2.getElapsedTime()).compareTo(Long.valueOf(p1.getElapsedTime()));
-                                        }
-                                    });
-                                    for (TaskExecution taskExecution : taskExecutions) {
+                                    for (TaskExecution taskExecution : project.getTasks()) {
                                         htmlWriter.startElement("tr")
                                             .startElement("td").attribute("class", "indentPath").characters(taskExecution.getPath()).endElement()
                                             .startElement("td").attribute("class", "numeric").characters(DURATION_FORMAT.format(taskExecution.getElapsedTime())).endElement()
-                                            .startElement("td").characters(taskExecution.getState().getSkipped() ? taskExecution.getState().getSkipMessage() : (taskExecution.getState().getDidWork()) ? "" : "Did No Work").endElement()
+                                            .startElement("td").characters(taskExecution.getStatus()).endElement()
                                         .endElement();
                                     }
                                 }
diff --git a/subprojects/core/src/main/groovy/org/gradle/profile/ProjectProfile.java b/subprojects/core/src/main/groovy/org/gradle/profile/ProjectProfile.java
index e7abbe5..d0d2852 100644
--- a/subprojects/core/src/main/groovy/org/gradle/profile/ProjectProfile.java
+++ b/subprojects/core/src/main/groovy/org/gradle/profile/ProjectProfile.java
@@ -15,31 +15,29 @@
  */
 package org.gradle.profile;
 
-import org.gradle.api.Project;
-import org.gradle.api.ProjectState;
-import org.gradle.api.Task;
+import org.gradle.util.CollectionUtils;
 
 import java.util.HashMap;
+import java.util.List;
 
-public class ProjectProfile {
-    private final Project project;
-    private ProjectState state;
-    private HashMap<Task, TaskExecution> tasks = new HashMap<Task, TaskExecution>();
-    private final ContinuousOperation evaluation;
+public class ProjectProfile extends Operation {
+    private HashMap<String, TaskExecution> tasks = new HashMap<String, TaskExecution>();
+    private final ContinuousOperation configurationOperation;
+    private String projectPath;
 
-    public ProjectProfile(Project project) {
-        this.project = project;
-        this.evaluation = new EvalutationOperation(project);
+    public ProjectProfile(String projectPath) {
+        this.projectPath = projectPath;
+        this.configurationOperation = new ContinuousOperation(projectPath);
     }
 
     /**
      * Gets the task profiling container for the specified task.
      */
-    public TaskExecution getTaskProfile(Task task) {
-        TaskExecution result = tasks.get(task);
+    public TaskExecution getTaskProfile(String taskPath) {
+        TaskExecution result = tasks.get(taskPath);
         if (result == null) {
-            result = new TaskExecution(task);
-            tasks.put(task, result);
+            result = new TaskExecution(taskPath);
+            tasks.put(taskPath, result);
         }
         return result;
     }
@@ -48,31 +46,33 @@ public class ProjectProfile {
      * Returns the task executions for this project.
      */
     public CompositeOperation<TaskExecution> getTasks() {
-        return new CompositeOperation<TaskExecution>(tasks.values());
+        List<TaskExecution> taskExecutions = CollectionUtils.sort(tasks.values(), Operation.slowestFirst());
+        return new CompositeOperation<TaskExecution>(taskExecutions);
     }
 
     /**
      * Get the String project path.
      */
     public String getPath() {
-        return project.getPath();
+        return projectPath;
     }
 
     /**
-     * Returns the evaluation time of this project.
+     * Returns the configuration time of this project.
      */
-    public ContinuousOperation getEvaluation() {
-        return evaluation;
+    public ContinuousOperation getConfigurationOperation() {
+        return configurationOperation;
     }
 
-    /**
-     * Gets the state of the project after evaluation finishes.
-     */
-    public ProjectState getState() {
-        return state;
+    public String toString() {
+        return projectPath;
+    }
+
+    public String getDescription() {
+        return projectPath;
     }
 
-    public void setState(ProjectState state) {
-        this.state = state;
+    long getElapsedTime() {
+        return getTasks().getElapsedTime();
     }
 }
diff --git a/subprojects/core/src/main/groovy/org/gradle/profile/TaskExecution.java b/subprojects/core/src/main/groovy/org/gradle/profile/TaskExecution.java
index e322a82..2596879 100644
--- a/subprojects/core/src/main/groovy/org/gradle/profile/TaskExecution.java
+++ b/subprojects/core/src/main/groovy/org/gradle/profile/TaskExecution.java
@@ -15,34 +15,41 @@
  */
 package org.gradle.profile;
 
-import org.gradle.api.Task;
 import org.gradle.api.tasks.TaskState;
 
 /**
  * Container for task profiling information.
- * This includes timestamps around task execution and the resulting TaskState.
+ * This includes timestamps around task execution and the resulting task status.
  */
 public class TaskExecution extends ContinuousOperation {
-    private final Task task;
+
+    final static String NO_WORK_MESSAGE = "Did No Work";
+
+    private final String path;
     private TaskState state;
 
-    public TaskExecution(Task task) {
-        this.task = task;
+    public TaskExecution(String taskPath) {
+        super(taskPath);
+        this.path = taskPath;
     }
 
     /**
      * Gets the string task path.
-     * @return
      */
     public String getPath() {
-        return task.getPath();
+        return path;
+    }
+
+    public String getStatus() {
+        return state.getSkipped() ? state.getSkipMessage() : (state.getDidWork()) ? "" : NO_WORK_MESSAGE;
     }
 
     public TaskState getState() {
         return state;
     }
 
-    public void setState(TaskState state) {
+    public TaskExecution completed(TaskState state) {
         this.state = state;
+        return this;
     }
 }
diff --git a/subprojects/core/src/main/groovy/org/gradle/reporting/ReportRenderer.java b/subprojects/core/src/main/groovy/org/gradle/reporting/ReportRenderer.java
index 328acff..d3142c8 100644
--- a/subprojects/core/src/main/groovy/org/gradle/reporting/ReportRenderer.java
+++ b/subprojects/core/src/main/groovy/org/gradle/reporting/ReportRenderer.java
@@ -20,7 +20,7 @@ import java.io.IOException;
 
 public abstract class ReportRenderer<T, E> {
     /**
-     * Renders the report for the given model as children of the given DOM element.
+     * Renders the report for the given model to the given output.
      */
-    public abstract void render(T model, E parent) throws IOException;
+    public abstract void render(T model, E output) throws IOException;
 }
\ No newline at end of file
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
deleted file mode 100644
index 168bc85..0000000
--- a/subprojects/core/src/main/groovy/org/gradle/testfixtures/internal/GlobalTestServices.java
+++ /dev/null
@@ -1,60 +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.testfixtures.internal;
-
-import org.gradle.api.internal.project.GlobalServicesRegistry;
-import org.gradle.internal.Factory;
-import org.gradle.internal.TrueTimeProvider;
-import org.gradle.internal.service.DefaultServiceRegistry;
-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;
-
-public class GlobalTestServices extends GlobalServicesRegistry {
-    public GlobalTestServices() {
-        super(new TestLoggingServices());
-    }
-
-    private static class TestLoggingServices extends DefaultServiceRegistry {
-        final ListenerManager listenerManager = new DefaultListenerManager();
-
-        protected ProgressLoggerFactory createProgressLoggerFactory() {
-            return new DefaultProgressLoggerFactory(listenerManager.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(listenerManager.getBroadcaster(OutputEventListener.class), new TrueTimeProvider());
-        }
-
-        protected TestOutputEventListener createStubOutputEventListener() {
-            return new TestOutputEventListener();
-        }
-    }
-}
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
index 3f802b2..b9f5d27 100644
--- a/subprojects/core/src/main/groovy/org/gradle/testfixtures/internal/InMemoryCacheFactory.java
+++ b/subprojects/core/src/main/groovy/org/gradle/testfixtures/internal/InMemoryCacheFactory.java
@@ -17,7 +17,6 @@ package org.gradle.testfixtures.internal;
 
 import org.gradle.CacheUsage;
 import org.gradle.api.Action;
-import org.gradle.api.internal.changedetection.InMemoryIndexedCache;
 import org.gradle.cache.*;
 import org.gradle.cache.internal.*;
 import org.gradle.internal.Factory;
diff --git a/subprojects/core/src/main/groovy/org/gradle/testfixtures/internal/InMemoryIndexedCache.java b/subprojects/core/src/main/groovy/org/gradle/testfixtures/internal/InMemoryIndexedCache.java
new file mode 100644
index 0000000..cdea768
--- /dev/null
+++ b/subprojects/core/src/main/groovy/org/gradle/testfixtures/internal/InMemoryIndexedCache.java
@@ -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.testfixtures.internal;
+
+import org.gradle.cache.PersistentIndexedCache;
+import org.gradle.internal.UncheckedException;
+import org.gradle.messaging.serialize.InputStreamBackedDecoder;
+import org.gradle.messaging.serialize.OutputStreamBackedEncoder;
+import org.gradle.messaging.serialize.Serializer;
+
+import java.io.ByteArrayInputStream;
+import java.io.ByteArrayOutputStream;
+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> {
+    private final Map<Object, byte[]> entries = new HashMap<Object, byte[]>();
+    private final Serializer<V> valueSerializer;
+
+    public InMemoryIndexedCache(Serializer<V> valueSerializer) {
+        this.valueSerializer = valueSerializer;
+    }
+
+    public V get(K key) {
+        byte[] serialised = entries.get(key);
+        if (serialised == null) {
+            return null;
+        }
+        try {
+            ByteArrayInputStream instr = new ByteArrayInputStream(serialised);
+            InputStreamBackedDecoder decoder = new InputStreamBackedDecoder(instr);
+            return valueSerializer.read(decoder);
+        } catch (Exception e) {
+            throw UncheckedException.throwAsUncheckedException(e);
+        }
+    }
+
+    public void put(K key, V value) {
+        ByteArrayOutputStream outstr = new ByteArrayOutputStream();
+        OutputStreamBackedEncoder encoder = new OutputStreamBackedEncoder(outstr);
+        try {
+            valueSerializer.write(encoder, value);
+            encoder.flush();
+        } catch (Exception e) {
+            throw UncheckedException.throwAsUncheckedException(e);
+        }
+
+        entries.put(key, outstr.toByteArray());
+    }
+
+    public void remove(K key) {
+        entries.remove(key);
+    }
+}
diff --git a/subprojects/core/src/main/groovy/org/gradle/testfixtures/internal/ProjectBuilderImpl.java b/subprojects/core/src/main/groovy/org/gradle/testfixtures/internal/ProjectBuilderImpl.java
index 51a8bb2..42cbe8a 100644
--- a/subprojects/core/src/main/groovy/org/gradle/testfixtures/internal/ProjectBuilderImpl.java
+++ b/subprojects/core/src/main/groovy/org/gradle/testfixtures/internal/ProjectBuilderImpl.java
@@ -20,25 +20,25 @@ import org.gradle.StartParameter;
 import org.gradle.api.Project;
 import org.gradle.api.internal.AsmBackedClassGenerator;
 import org.gradle.api.internal.GradleInternal;
+import org.gradle.api.internal.file.BaseDirFileResolver;
 import org.gradle.api.internal.file.TemporaryFileProvider;
 import org.gradle.api.internal.file.TmpDirTemporaryFileProvider;
 import org.gradle.api.internal.project.DefaultProject;
 import org.gradle.api.internal.project.IProjectFactory;
 import org.gradle.api.internal.project.ProjectInternal;
-import org.gradle.api.internal.project.ServiceRegistryFactory;
+import org.gradle.internal.nativeplatform.filesystem.FileSystems;
+import org.gradle.internal.service.scopes.ServiceRegistryFactory;
 import org.gradle.groovy.scripts.StringScriptSource;
 import org.gradle.initialization.DefaultProjectDescriptor;
 import org.gradle.initialization.DefaultProjectDescriptorRegistry;
+import org.gradle.invocation.BuildClassLoaderRegistry;
 import org.gradle.invocation.DefaultGradle;
 import org.gradle.util.GFileUtils;
 
 import java.io.File;
 
-/**
- * by Szczepan Faber, created at: 10/1/11
- */
 public class ProjectBuilderImpl {
-    private static final GlobalTestServices GLOBAL_SERVICES = new GlobalTestServices();
+    private static final TestGlobalScopeServices GLOBAL_SERVICES = new TestGlobalScopeServices();
     private static final AsmBackedClassGenerator CLASS_GENERATOR = new AsmBackedClassGenerator();
 
     public Project createChildProject(String name, Project parent, File projectDir) {
@@ -65,16 +65,17 @@ public class ProjectBuilderImpl {
         StartParameter startParameter = new StartParameter();
         startParameter.setGradleUserHomeDir(new File(projectDir, "userHome"));
 
-        ServiceRegistryFactory topLevelRegistry = new TestTopLevelBuildServiceRegistry(GLOBAL_SERVICES, startParameter, homeDir);
+        ServiceRegistryFactory topLevelRegistry = new TestBuildScopeServices(GLOBAL_SERVICES, startParameter, homeDir);
         GradleInternal gradle = new DefaultGradle(null, startParameter, topLevelRegistry);
 
-        DefaultProjectDescriptor projectDescriptor = new DefaultProjectDescriptor(null, name, projectDir, new DefaultProjectDescriptorRegistry());
+        DefaultProjectDescriptor projectDescriptor = new DefaultProjectDescriptor(null, name, projectDir, new DefaultProjectDescriptorRegistry(),
+                new BaseDirFileResolver(FileSystems.getDefault(), projectDir));
         ProjectInternal project = topLevelRegistry.get(IProjectFactory.class).createProject(projectDescriptor, null, gradle);
 
         gradle.setRootProject(project);
         gradle.setDefaultProject(project);
 
-        gradle.getScriptClassLoader().addParent(getClass().getClassLoader());
+        gradle.getServices().get(BuildClassLoaderRegistry.class).addRootClassLoader(getClass().getClassLoader());
 
         return project;
     }
diff --git a/subprojects/core/src/main/groovy/org/gradle/testfixtures/internal/TestBuildScopeServices.java b/subprojects/core/src/main/groovy/org/gradle/testfixtures/internal/TestBuildScopeServices.java
new file mode 100644
index 0000000..6fd3af3
--- /dev/null
+++ b/subprojects/core/src/main/groovy/org/gradle/testfixtures/internal/TestBuildScopeServices.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.testfixtures.internal;
+
+import org.gradle.StartParameter;
+import org.gradle.api.internal.GradleDistributionLocator;
+import org.gradle.internal.service.scopes.BuildScopeServices;
+import org.gradle.cache.internal.CacheFactory;
+import org.gradle.configuration.GradleLauncherMetaData;
+import org.gradle.initialization.BuildClientMetaData;
+import org.gradle.internal.service.ServiceRegistry;
+
+import java.io.File;
+
+public class TestBuildScopeServices extends BuildScopeServices {
+    private final File homeDir;
+
+    public TestBuildScopeServices(ServiceRegistry parent, StartParameter startParameter, File homeDir) {
+        super(parent, startParameter);
+        this.homeDir = homeDir;
+    }
+
+    protected BuildClientMetaData createClientMetaData() {
+        return new GradleLauncherMetaData();
+    }
+
+    @Override
+    protected CacheFactory createCacheFactory() {
+        return new InMemoryCacheFactory();
+    }
+
+    protected GradleDistributionLocator createGradleDistributionLocator() {
+        return new GradleDistributionLocator() {
+            public File getGradleHome() {
+                return homeDir;
+            }
+        };
+    }
+}
diff --git a/subprojects/core/src/main/groovy/org/gradle/testfixtures/internal/TestGlobalScopeServices.java b/subprojects/core/src/main/groovy/org/gradle/testfixtures/internal/TestGlobalScopeServices.java
new file mode 100644
index 0000000..72c3aea
--- /dev/null
+++ b/subprojects/core/src/main/groovy/org/gradle/testfixtures/internal/TestGlobalScopeServices.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.testfixtures.internal;
+
+import org.gradle.internal.service.scopes.GlobalScopeServices;
+import org.gradle.internal.Factory;
+import org.gradle.internal.TrueTimeProvider;
+import org.gradle.internal.service.DefaultServiceRegistry;
+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;
+
+public class TestGlobalScopeServices extends GlobalScopeServices {
+    public TestGlobalScopeServices() {
+        super(new TestLoggingServices());
+    }
+
+    private static class TestLoggingServices extends DefaultServiceRegistry {
+        final ListenerManager listenerManager = new DefaultListenerManager();
+
+        protected ProgressLoggerFactory createProgressLoggerFactory() {
+            return new DefaultProgressLoggerFactory(listenerManager.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(listenerManager.getBroadcaster(OutputEventListener.class), new TrueTimeProvider());
+        }
+
+        protected TestOutputEventListener createStubOutputEventListener() {
+            return new TestOutputEventListener();
+        }
+    }
+}
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
deleted file mode 100644
index 09bc272..0000000
--- a/subprojects/core/src/main/groovy/org/gradle/testfixtures/internal/TestTopLevelBuildServiceRegistry.java
+++ /dev/null
@@ -1,52 +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.testfixtures.internal;
-
-import org.gradle.StartParameter;
-import org.gradle.api.internal.GradleDistributionLocator;
-import org.gradle.api.internal.project.TopLevelBuildServiceRegistry;
-import org.gradle.cache.internal.CacheFactory;
-import org.gradle.configuration.GradleLauncherMetaData;
-import org.gradle.initialization.BuildClientMetaData;
-import org.gradle.internal.service.ServiceRegistry;
-
-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();
-    }
-
-    @Override
-    protected CacheFactory createCacheFactory() {
-        return new InMemoryCacheFactory();
-    }
-
-    protected GradleDistributionLocator createGradleDistributionLocator() {
-        return new GradleDistributionLocator() {
-            public File getGradleHome() {
-                return homeDir;
-            }
-        };
-    }
-}
diff --git a/subprojects/core/src/main/groovy/org/gradle/tooling/provider/model/ToolingModelBuilder.java b/subprojects/core/src/main/groovy/org/gradle/tooling/provider/model/ToolingModelBuilder.java
new file mode 100644
index 0000000..2b394b5
--- /dev/null
+++ b/subprojects/core/src/main/groovy/org/gradle/tooling/provider/model/ToolingModelBuilder.java
@@ -0,0 +1,29 @@
+/*
+ * Copyright 2012 the original author or authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS 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.provider.model;
+
+import org.gradle.api.Incubating;
+import org.gradle.api.Project;
+
+/**
+ * Responsible for building tooling models.
+ */
+ at Incubating
+public interface ToolingModelBuilder {
+    boolean canBuild(String modelName);
+    Object buildAll(String modelName, Project project);
+}
diff --git a/subprojects/core/src/main/groovy/org/gradle/tooling/provider/model/ToolingModelBuilderRegistry.java b/subprojects/core/src/main/groovy/org/gradle/tooling/provider/model/ToolingModelBuilderRegistry.java
new file mode 100644
index 0000000..5757d23
--- /dev/null
+++ b/subprojects/core/src/main/groovy/org/gradle/tooling/provider/model/ToolingModelBuilderRegistry.java
@@ -0,0 +1,29 @@
+/*
+ * Copyright 2012 the original author or authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS 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.provider.model;
+
+import org.gradle.api.Incubating;
+
+/**
+ * A registry of tooling model builders. Adding a builder to this registry makes a model (or models) available via the tooling API.
+ */
+ at Incubating
+public interface ToolingModelBuilderRegistry {
+    void register(ToolingModelBuilder builder);
+
+    ToolingModelBuilder getBuilder(String modelName) throws UnknownModelException;
+}
diff --git a/subprojects/core/src/main/groovy/org/gradle/tooling/provider/model/UnknownModelException.java b/subprojects/core/src/main/groovy/org/gradle/tooling/provider/model/UnknownModelException.java
new file mode 100644
index 0000000..f66d460
--- /dev/null
+++ b/subprojects/core/src/main/groovy/org/gradle/tooling/provider/model/UnknownModelException.java
@@ -0,0 +1,31 @@
+/*
+ * Copyright 2013 the original author or authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS 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.provider.model;
+
+import org.gradle.api.GradleException;
+import org.gradle.api.Incubating;
+
+/**
+ * Thrown when an unknown tooling model is requested.
+ */
+ at Incubating
+public class UnknownModelException extends GradleException {
+    public UnknownModelException(String message) {
+        super(message);
+    }
+}
+
diff --git a/subprojects/core/src/main/groovy/org/gradle/tooling/provider/model/internal/DefaultToolingModelBuilderRegistry.java b/subprojects/core/src/main/groovy/org/gradle/tooling/provider/model/internal/DefaultToolingModelBuilderRegistry.java
new file mode 100644
index 0000000..2837773
--- /dev/null
+++ b/subprojects/core/src/main/groovy/org/gradle/tooling/provider/model/internal/DefaultToolingModelBuilderRegistry.java
@@ -0,0 +1,64 @@
+/*
+ * Copyright 2012 the original author or authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS 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.provider.model.internal;
+
+import org.gradle.api.Project;
+import org.gradle.tooling.provider.model.ToolingModelBuilder;
+import org.gradle.tooling.provider.model.ToolingModelBuilderRegistry;
+import org.gradle.tooling.provider.model.UnknownModelException;
+
+import java.util.ArrayList;
+import java.util.List;
+
+public class DefaultToolingModelBuilderRegistry implements ToolingModelBuilderRegistry {
+    private final List<ToolingModelBuilder> builders = new ArrayList<ToolingModelBuilder>();
+
+    public DefaultToolingModelBuilderRegistry() {
+        register(new VoidToolingModelBuilder());
+    }
+
+    public void register(ToolingModelBuilder builder) {
+        builders.add(builder);
+    }
+
+    public ToolingModelBuilder getBuilder(String modelName) throws UnsupportedOperationException {
+        ToolingModelBuilder match = null;
+        for (ToolingModelBuilder builder : builders) {
+            if (builder.canBuild(modelName)) {
+                if (match != null) {
+                    throw new UnsupportedOperationException(String.format("Multiple builders are available to build a model of type '%s'.", modelName));
+                }
+                match = builder;
+            }
+        }
+        if (match != null) {
+            return match;
+        }
+
+        throw new UnknownModelException(String.format("No builders are available to build a model of type '%s'.", modelName));
+    }
+
+    private static class VoidToolingModelBuilder implements ToolingModelBuilder {
+        public boolean canBuild(String modelName) {
+            return modelName.equals(Void.class.getName());
+        }
+
+        public Object buildAll(String modelName, Project project) {
+            return null;
+        }
+    }
+}
diff --git a/subprojects/core/src/main/groovy/org/gradle/tooling/provider/model/internal/LegacyConsumerInterface.java b/subprojects/core/src/main/groovy/org/gradle/tooling/provider/model/internal/LegacyConsumerInterface.java
new file mode 100644
index 0000000..d58fc69
--- /dev/null
+++ b/subprojects/core/src/main/groovy/org/gradle/tooling/provider/model/internal/LegacyConsumerInterface.java
@@ -0,0 +1,25 @@
+/*
+ * Copyright 2013 the original author or authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS 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.provider.model.internal;
+
+/**
+ * Indicates that a given marker interface should be mixed in to instances of the annotated type before they
+ * are passed to the tooling API client.
+ */
+public @interface LegacyConsumerInterface {
+    String value();
+}
diff --git a/subprojects/core/src/main/groovy/org/gradle/tooling/provider/model/package-info.java b/subprojects/core/src/main/groovy/org/gradle/tooling/provider/model/package-info.java
new file mode 100644
index 0000000..ae44366
--- /dev/null
+++ b/subprojects/core/src/main/groovy/org/gradle/tooling/provider/model/package-info.java
@@ -0,0 +1,20 @@
+/*
+ * Copyright 2013 the original author or authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+/**
+ * Interfaces and classes that allow tooling models to be made available to the tooling API client.
+ */
+package org.gradle.tooling.provider.model;
diff --git a/subprojects/core/src/main/groovy/org/gradle/util/AntUtil.java b/subprojects/core/src/main/groovy/org/gradle/util/AntUtil.java
index fc621f3..34fa854 100644
--- a/subprojects/core/src/main/groovy/org/gradle/util/AntUtil.java
+++ b/subprojects/core/src/main/groovy/org/gradle/util/AntUtil.java
@@ -15,12 +15,11 @@
  */
 package org.gradle.util;
 
-import org.apache.tools.ant.*;
+import org.apache.tools.ant.Project;
+import org.apache.tools.ant.ProjectHelper;
+import org.apache.tools.ant.Task;
 import org.gradle.api.internal.project.ant.AntLoggingAdapter;
 
-/**
- * @author Hans Dockter
- */
 public class AntUtil {
     /**
      * @return Factory method to create new Project instances
diff --git a/subprojects/core/src/main/groovy/org/gradle/util/AvailablePortFinder.java b/subprojects/core/src/main/groovy/org/gradle/util/AvailablePortFinder.java
index fd7efdd..677f86f 100644
--- a/subprojects/core/src/main/groovy/org/gradle/util/AvailablePortFinder.java
+++ b/subprojects/core/src/main/groovy/org/gradle/util/AvailablePortFinder.java
@@ -31,8 +31,6 @@ import java.util.concurrent.locks.ReentrantLock;
  *
  * <em>Note:</em> If possible, it's preferable to let the party creating the server socket select the port (e.g. with <tt>new ServerSocket(0)</tt>) and then query it for the port chosen. With this
  * class, there is always a risk that someone else grabs the port between the time it is returned from <tt>getNextAvailable()</tt> and the time the socket is created.
- *
- * @author <a href="http://mina.apache.org">Apache MINA Project</a>
  * @see <a href="http://www.iana.org/assignments/port-numbers">IANA.org</a>
  */
 @ThreadSafe
diff --git a/subprojects/core/src/main/groovy/org/gradle/util/ClassLoaderBackedClasspathSource.java b/subprojects/core/src/main/groovy/org/gradle/util/ClassLoaderBackedClasspathSource.java
deleted file mode 100644
index a83a233..0000000
--- a/subprojects/core/src/main/groovy/org/gradle/util/ClassLoaderBackedClasspathSource.java
+++ /dev/null
@@ -1,43 +0,0 @@
-/*
- * Copyright 2012 the original author or authors.
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- *      http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-package org.gradle.util;
-
-import java.net.URL;
-import java.net.URLClassLoader;
-import java.util.Arrays;
-import java.util.Collection;
-
-public class ClassLoaderBackedClasspathSource implements ClasspathSource {
-    private final ClassLoader classLoader;
-
-    public ClassLoaderBackedClasspathSource(ClassLoader classLoader) {
-        this.classLoader = classLoader;
-    }
-
-    public void collectClasspath(Collection<? super URL> classpath) {
-        ClassLoader stopAt = ClassLoader.getSystemClassLoader() == null ? null : ClassLoader.getSystemClassLoader().getParent();
-        for (ClassLoader cl = classLoader; cl != null && cl != stopAt; cl = cl.getParent()) {
-            if (cl instanceof ClasspathSource) {
-                ClasspathSource classpathSource = (ClasspathSource) cl;
-                classpathSource.collectClasspath(classpath);
-                break;
-            }
-            if (cl instanceof URLClassLoader) {
-                classpath.addAll(Arrays.asList(((URLClassLoader) cl).getURLs()));
-            }
-        }
-    }
-}
diff --git a/subprojects/core/src/main/groovy/org/gradle/util/ClassLoaderFactory.java b/subprojects/core/src/main/groovy/org/gradle/util/ClassLoaderFactory.java
deleted file mode 100644
index 084dc82..0000000
--- a/subprojects/core/src/main/groovy/org/gradle/util/ClassLoaderFactory.java
+++ /dev/null
@@ -1,41 +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.util;
-
-import org.gradle.internal.classpath.ClassPath;
-
-import java.net.URI;
-
-public interface ClassLoaderFactory {
-    /**
-     * Creates a ClassLoader implementation which has only the classes from the specified URIs and the Java API visible.
-     */
-    ClassLoader createIsolatedClassLoader(ClassPath classPath);
-
-    /**
-     * Creates a ClassLoader implementation which has only the classes from the specified URIs and the Java API visible.
-     */
-    ClassLoader createIsolatedClassLoader(Iterable<URI> uris);
-
-    /**
-     * Creates a ClassLoader implementation which has, by default, only the classes from the Java API visible, but which can allow access
-     * to selected classes from the given parent ClassLoader.
-     *
-     * @param parent the parent ClassLoader
-     * @return The ClassLoader
-     */
-    FilteringClassLoader createFilteringClassLoader(ClassLoader parent);
-}
diff --git a/subprojects/core/src/main/groovy/org/gradle/util/ClasspathSource.java b/subprojects/core/src/main/groovy/org/gradle/util/ClasspathSource.java
deleted file mode 100644
index c47980f..0000000
--- a/subprojects/core/src/main/groovy/org/gradle/util/ClasspathSource.java
+++ /dev/null
@@ -1,23 +0,0 @@
-/*
- * Copyright 2012 the original author or authors.
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- *      http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-package org.gradle.util;
-
-import java.net.URL;
-import java.util.Collection;
-
-public interface ClasspathSource {
-    void collectClasspath(Collection<? super URL> classpath);
-}
diff --git a/subprojects/core/src/main/groovy/org/gradle/util/ClasspathUtil.java b/subprojects/core/src/main/groovy/org/gradle/util/ClasspathUtil.java
deleted file mode 100644
index a80bec4..0000000
--- a/subprojects/core/src/main/groovy/org/gradle/util/ClasspathUtil.java
+++ /dev/null
@@ -1,98 +0,0 @@
-/*
- * Copyright 2010 the original author or authors.
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- *      http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package org.gradle.util;
-
-import org.gradle.api.GradleException;
-import org.gradle.internal.UncheckedException;
-
-import java.io.File;
-import java.lang.reflect.Method;
-import java.net.URI;
-import java.net.URISyntaxException;
-import java.net.URL;
-import java.net.URLClassLoader;
-import java.util.ArrayList;
-import java.util.List;
-
-/**
- * @author Hans Dockter
- */
-public class ClasspathUtil {
-    public static void addUrl(URLClassLoader classLoader, Iterable<URL> classpathElements) {
-        try {
-            Method method = URLClassLoader.class.getDeclaredMethod("addURL", URL.class);
-            method.setAccessible(true);
-            for (URL classpathElement : classpathElements) {
-                method.invoke(classLoader, classpathElement);
-            }
-        } catch (Throwable 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>();
-        new ClassLoaderBackedClasspathSource(classLoader).collectClasspath(implementationClassPath);
-        return implementationClassPath;
-    }
-
-    public static File getClasspathForClass(Class<?> targetClass) {
-        URI location;
-        try {
-            location = targetClass.getProtectionDomain().getCodeSource().getLocation().toURI();
-        } catch (URISyntaxException e) {
-            throw UncheckedException.throwAsUncheckedException(e);
-        }
-        if (!location.getScheme().equals("file")) {
-            throw new GradleException(String.format("Cannot determine classpath for %s from codebase '%s'.", targetClass.getName(), location));
-        }
-        return new File(location.getPath());
-    }
-
-    public static File getClasspathForResource(ClassLoader classLoader, String name) {
-        if (classLoader == null) {
-            return getClasspathForResource(ClassLoader.getSystemResource(name), name);
-        } else {
-            return getClasspathForResource(classLoader.getResource(name), name);
-        }
-    }
-
-    public static File getClasspathForResource(URL resource, String name) {
-        URI location;
-        try {
-            location = resource.toURI();
-            String path = location.getPath();
-            if (location.getScheme().equals("file")) {
-                assert path.endsWith("/" + name);
-                return new File(path.substring(0, path.length() - (name.length() + 1)));
-            } else if (location.getScheme().equals("jar")) {
-                String schemeSpecificPart = location.getRawSchemeSpecificPart();
-                int pos = schemeSpecificPart.indexOf("!");
-                if (pos > 0) {
-                    assert schemeSpecificPart.substring(pos + 1).equals("/" + name);
-                    URI jarFile = new URI(schemeSpecificPart.substring(0, pos));
-                    if (jarFile.getScheme().equals("file")) {
-                        return new File(jarFile.getPath());
-                    }
-                }
-            }
-        } catch (URISyntaxException e) {
-            throw UncheckedException.throwAsUncheckedException(e);
-        }
-        throw new GradleException(String.format("Cannot determine classpath for resource '%s' from location '%s'.", name, location));
-    }
-}
diff --git a/subprojects/core/src/main/groovy/org/gradle/util/Clock.java b/subprojects/core/src/main/groovy/org/gradle/util/Clock.java
index 317cff3..adf17dd 100644
--- a/subprojects/core/src/main/groovy/org/gradle/util/Clock.java
+++ b/subprojects/core/src/main/groovy/org/gradle/util/Clock.java
@@ -19,9 +19,6 @@ package org.gradle.util;
 import org.gradle.internal.TimeProvider;
 import org.gradle.internal.TrueTimeProvider;
 
-/**
- * @author Hans Dockter
- */
 public class Clock {
     private long start;
     private TimeProvider timeProvider;
diff --git a/subprojects/core/src/main/groovy/org/gradle/util/ConfigureUtil.java b/subprojects/core/src/main/groovy/org/gradle/util/ConfigureUtil.java
index f70c538..482fd06 100644
--- a/subprojects/core/src/main/groovy/org/gradle/util/ConfigureUtil.java
+++ b/subprojects/core/src/main/groovy/org/gradle/util/ConfigureUtil.java
@@ -27,9 +27,6 @@ import java.util.Map;
 
 import static org.gradle.util.CollectionUtils.toStringList;
 
-/**
- * @author Hans Dockter
- */
 public class ConfigureUtil {
 
     public static <T> T configureByMap(Map<?, ?> properties, T delegate) {
diff --git a/subprojects/core/src/main/groovy/org/gradle/util/DefaultClassLoaderFactory.java b/subprojects/core/src/main/groovy/org/gradle/util/DefaultClassLoaderFactory.java
deleted file mode 100644
index 684e616..0000000
--- a/subprojects/core/src/main/groovy/org/gradle/util/DefaultClassLoaderFactory.java
+++ /dev/null
@@ -1,86 +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.util;
-
-import org.gradle.internal.UncheckedException;
-import org.gradle.internal.classpath.ClassPath;
-import org.gradle.internal.service.ServiceLocator;
-
-import javax.xml.datatype.DatatypeFactory;
-import javax.xml.parsers.DocumentBuilderFactory;
-import javax.xml.parsers.SAXParserFactory;
-import java.net.MalformedURLException;
-import java.net.URI;
-import java.net.URL;
-import java.net.URLClassLoader;
-import java.util.Collection;
-
-public class DefaultClassLoaderFactory implements ClassLoaderFactory {
-    public ClassLoader createIsolatedClassLoader(Iterable<URI> uris) {
-        return doCreateIsolatedClassLoader(GFileUtils.urisToUrls(uris));
-    }
-
-    public ClassLoader createIsolatedClassLoader(ClassPath classPath) {
-        return doCreateIsolatedClassLoader(classPath.getAsURLs());
-    }
-
-    private ClassLoader doCreateIsolatedClassLoader(Collection<URL> classpath) {
-        // This piece of ugliness copies the JAXP (ie XML API) provider, if any, from the system ClassLoader. Here's why:
-        //
-        // 1. When looking for a provider, JAXP looks for a service resource in the context ClassLoader, which is our isolated ClassLoader. If our classpath above does not contain a
-        //    provider, this returns null. If it does contain a provider, JAXP extracts the classname from the service resource.
-        // 2. If not found, JAXP looks for a service resource in the system ClassLoader. This happens to include all the application classes specified on the classpath. If the application
-        //    classpath does not contain a provider, this returns null. If it does contain a provider, JAXP extracts the implementation classname from the service resource.
-        // 3. If not found, JAXP uses a default classname
-        // 4. JAXP attempts to load the provider using the context ClassLoader. which is our isolated ClassLoader. This is fine if the classname came from step 1 or 3. It blows up if the
-        //    classname came from step 2.
-        //
-        // So, as a workaround, locate and include the JAXP provider jar in the classpath for our isolated ClassLoader.
-        //
-        // Note that in practise, this is only triggered when running in our tests
-
-        if (needJaxpImpl()) {
-            try {
-                classpath.add(ClasspathUtil.getClasspathForResource(ClassLoader.getSystemClassLoader(), "META-INF/services/javax.xml.parsers.SAXParserFactory").toURI().toURL());
-            } catch (MalformedURLException e) {
-                throw UncheckedException.throwAsUncheckedException(e);
-            }
-        }
-
-        return new URLClassLoader(classpath.toArray(new URL[classpath.size()]), ClassLoader.getSystemClassLoader().getParent());
-    }
-
-    public FilteringClassLoader createFilteringClassLoader(ClassLoader parent) {
-        // See the comment for {@link #createIsolatedClassLoader} above
-        FilteringClassLoader classLoader = new FilteringClassLoader(parent);
-        if (needJaxpImpl()) {
-            ServiceLocator locator = new ServiceLocator(ClassLoader.getSystemClassLoader());
-            makeServiceVisible(locator, classLoader, SAXParserFactory.class);
-            makeServiceVisible(locator, classLoader, DocumentBuilderFactory.class);
-            makeServiceVisible(locator, classLoader, DatatypeFactory.class);
-        }
-        return classLoader;
-    }
-
-    private void makeServiceVisible(ServiceLocator locator, FilteringClassLoader classLoader, Class<?> serviceType) {
-        classLoader.allowClass(locator.getFactory(serviceType).getImplementationClass());
-        classLoader.allowResource("META-INF/services/" + serviceType.getName());
-    }
-
-    private boolean needJaxpImpl() {
-        return ClassLoader.getSystemResource("META-INF/services/javax.xml.parsers.SAXParserFactory") != null;
-    }
-}
diff --git a/subprojects/core/src/main/groovy/org/gradle/util/DeleteOnExit.java b/subprojects/core/src/main/groovy/org/gradle/util/DeleteOnExit.java
deleted file mode 100644
index a16ac23..0000000
--- a/subprojects/core/src/main/groovy/org/gradle/util/DeleteOnExit.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.util;
-
-import org.apache.commons.io.FileUtils;
-
-import java.util.ArrayList;
-import java.io.File;
-
-/**
- * Provides a mechanism to delete files or whole directories on shutdown.
- * File.deleteOnExit won't work on subdirectories that are not empty.
- * There are some temporary files which are not currently well managed
- * but we want to make sure that they are eventually removed
- * @author Steve Appling
- */
-public class DeleteOnExit {
-    private static final ArrayList<File> FILES = new ArrayList<File>();
-
-    static {
-        Runtime.getRuntime().addShutdownHook(new DeleteOnExitThread());
-    }
-
-    public static void addFile(File file) {
-        synchronized (FILES) {
-            FILES.add(file);
-        }
-    }
-
-    private static class DeleteOnExitThread extends Thread {
-        public void run() {
-            synchronized (FILES) {
-                for (File file : FILES) {
-                    FileUtils.deleteQuietly(file);
-                }
-            }
-        }
-    }
-}
diff --git a/subprojects/core/src/main/groovy/org/gradle/util/FilteringClassLoader.java b/subprojects/core/src/main/groovy/org/gradle/util/FilteringClassLoader.java
deleted file mode 100644
index ab2b12a..0000000
--- a/subprojects/core/src/main/groovy/org/gradle/util/FilteringClassLoader.java
+++ /dev/null
@@ -1,212 +0,0 @@
-/*
- * Copyright 2012 the original author or authors.
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- *      http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package org.gradle.util;
-
-import java.io.IOException;
-import java.net.URL;
-import java.security.AccessController;
-import java.security.PrivilegedAction;
-import java.util.*;
-
-/**
- * A ClassLoader which hides all non-system classes, packages and resources. Allows certain non-system packages and classes to be declared as visible. By default, only the Java system classes,
- * packages and resources are visible.
- */
-public class FilteringClassLoader extends ClassLoader {
-    private static final Set<ClassLoader> SYSTEM_CLASS_LOADERS = new HashSet<ClassLoader>();
-    private static final ClassLoader EXT_CLASS_LOADER;
-    private static final Set<String> SYSTEM_PACKAGES = new HashSet<String>();
-    private final Set<String> packageNames = new HashSet<String>();
-    private final Set<String> packagePrefixes = new HashSet<String>();
-    private final Set<String> resourcePrefixes = new HashSet<String>();
-    private final Set<String> resourceNames = new HashSet<String>();
-    private final Set<String> classNames = new HashSet<String>();
-    private final Set<String> disallowedClassNames = new HashSet<String>();
-
-    static {
-        EXT_CLASS_LOADER = ClassLoader.getSystemClassLoader().getParent();
-        for (ClassLoader cl = EXT_CLASS_LOADER; cl != null; cl = cl.getParent()) {
-            SYSTEM_CLASS_LOADERS.add(cl);
-        }
-        JavaMethod<ClassLoader, Package[]> method = JavaMethod.create(ClassLoader.class, Package[].class, "getPackages");
-        Package[] systemPackages = method.invoke(EXT_CLASS_LOADER);
-        for (Package p : systemPackages) {
-            SYSTEM_PACKAGES.add(p.getName());
-        }
-    }
-
-    public FilteringClassLoader(ClassLoader parent) {
-        super(parent);
-    }
-
-    @Override
-    protected Class<?> loadClass(String name, boolean resolve) throws ClassNotFoundException {
-        Class<?> cl;
-        try {
-            cl = super.loadClass(name, false);
-        } catch (NoClassDefFoundError e) {
-            if (classAllowed(name)) {
-                throw e;
-            }
-            // The class isn't visible
-            throw new ClassNotFoundException(String.format("%s not found.", name));
-        }
-
-        if (!allowed(cl)) {
-            throw new ClassNotFoundException(String.format("%s not found.", cl.getName()));
-        }
-        if (resolve) {
-            resolveClass(cl);
-        }
-
-        return cl;
-    }
-
-    @Override
-    protected Package getPackage(String name) {
-        Package p = super.getPackage(name);
-        if (p == null || !allowed(p)) {
-            return null;
-        }
-        return p;
-    }
-
-    @Override
-    protected Package[] getPackages() {
-        List<Package> packages = new ArrayList<Package>();
-        for (Package p : super.getPackages()) {
-            if (allowed(p)) {
-                packages.add(p);
-            }
-        }
-        return packages.toArray(new Package[packages.size()]);
-    }
-
-    @Override
-    public URL getResource(String name) {
-        if (allowed(name)) {
-            return super.getResource(name);
-        }
-        return EXT_CLASS_LOADER.getResource(name);
-    }
-
-    @Override
-    public Enumeration<URL> getResources(String name) throws IOException {
-        if (allowed(name)) {
-            return super.getResources(name);
-        }
-        return EXT_CLASS_LOADER.getResources(name);
-    }
-
-    private boolean allowed(String resourceName) {
-        if (resourceNames.contains(resourceName)) {
-            return true;
-        }
-        for (String resourcePrefix : resourcePrefixes) {
-            if (resourceName.startsWith(resourcePrefix)) {
-                return true;
-            }
-        }
-        return false;
-    }
-
-    private boolean allowed(Package pkg) {
-        if (SYSTEM_PACKAGES.contains(pkg.getName())) {
-            return true;
-        }
-        if (packageNames.contains(pkg.getName())) {
-            return true;
-        }
-        for (String packagePrefix : packagePrefixes) {
-            if (pkg.getName().startsWith(packagePrefix)) {
-                return true;
-            }
-        }
-        return false;
-    }
-
-    private boolean allowed(final Class<?> clazz) {
-        boolean systemClass = AccessController.doPrivileged(new PrivilegedAction<Boolean>() {
-            public Boolean run() {
-                return clazz.getClassLoader() == null || SYSTEM_CLASS_LOADERS.contains(clazz.getClassLoader());
-            }
-        });
-        return systemClass || classAllowed(clazz.getName());
-    }
-
-    private boolean classAllowed(String className) {
-        if (disallowedClassNames.contains(className)) {
-            return false;
-        }
-        if (classNames.contains(className)) {
-            return true;
-        }
-        for (String packagePrefix : packagePrefixes) {
-            if (className.startsWith(packagePrefix)) {
-                return true;
-            }
-        }
-        return false;
-    }
-
-    /**
-     * Marks a package and all its sub-packages as visible. Also makes resources in those packages visible.
-     *
-     * @param packageName the package name
-     */
-    public void allowPackage(String packageName) {
-        packageNames.add(packageName);
-        packagePrefixes.add(packageName + ".");
-        resourcePrefixes.add(packageName.replace('.', '/') + '/');
-    }
-
-    /**
-     * Marks a single class as visible.
-     *
-     * @param clazz the class
-     */
-    public void allowClass(Class<?> clazz) {
-        classNames.add(clazz.getName());
-    }
-
-    /**
-     * Marks a single class as not visible.
-     *
-     * @param className the class name
-     */
-    public void disallowClass(String className) {
-        disallowedClassNames.add(className);
-    }
-
-    /**
-     * Marks all resources with the given prefix as visible.
-     *
-     * @param resourcePrefix the resource prefix
-     */
-    public void allowResources(String resourcePrefix) {
-        resourcePrefixes.add(resourcePrefix + "/");
-    }
-
-    /**
-     * Marks a single resource as visible.
-     *
-     * @param resourceName the resource name
-     */
-    public void allowResource(String resourceName) {
-        resourceNames.add(resourceName);
-    }
-}
diff --git a/subprojects/core/src/main/groovy/org/gradle/util/GFileUtils.java b/subprojects/core/src/main/groovy/org/gradle/util/GFileUtils.java
index a08f5d9..a202a58 100644
--- a/subprojects/core/src/main/groovy/org/gradle/util/GFileUtils.java
+++ b/subprojects/core/src/main/groovy/org/gradle/util/GFileUtils.java
@@ -23,16 +23,11 @@ import org.gradle.internal.UncheckedException;
 import org.gradle.util.internal.LimitedDescription;
 
 import java.io.*;
-import java.net.MalformedURLException;
-import java.net.URI;
 import java.net.URL;
 import java.nio.charset.Charset;
 import java.util.*;
 import java.util.zip.Checksum;
 
-/**
- * @author Hans Dockter
- */
 public class GFileUtils {
 
     public static FileInputStream openInputStream(File file) {
@@ -114,18 +109,6 @@ public class GFileUtils {
         return paths;
     }
 
-    public static List<URL> urisToUrls(Iterable<URI> uris) {
-        List<URL> urls = new ArrayList<URL>();
-        for (URI uri : uris) {
-            try {
-                urls.add(uri.toURL());
-            } catch (MalformedURLException e) {
-                throw new UncheckedIOException(e);
-            }
-        }
-        return urls;
-    }
-
     public static void copyURLToFile(URL source, File destination) {
         try {
             FileUtils.copyURLToFile(source, destination);
diff --git a/subprojects/core/src/main/groovy/org/gradle/util/GStreamUtil.java b/subprojects/core/src/main/groovy/org/gradle/util/GStreamUtil.java
new file mode 100644
index 0000000..2e6d084
--- /dev/null
+++ b/subprojects/core/src/main/groovy/org/gradle/util/GStreamUtil.java
@@ -0,0 +1,33 @@
+/*
+ * Copyright 2013 the original author or authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.gradle.util;
+
+import java.io.DataInput;
+import java.io.IOException;
+
+public class GStreamUtil {
+
+    //skips 'n' bytes,
+    public static int skipBytes(int n, DataInput input) throws IOException {
+        int total = 0;
+        int cur;
+
+        while ((total<n) && ((cur = input.skipBytes(n - total)) > 0)) {
+            total += cur;
+        }
+        return total;
+    }
+}
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 e1be0fa..8f4adf5 100644
--- a/subprojects/core/src/main/groovy/org/gradle/util/GUtil.java
+++ b/subprojects/core/src/main/groovy/org/gradle/util/GUtil.java
@@ -29,9 +29,6 @@ import java.util.regex.Pattern;
 import static java.util.Arrays.asList;
 import static java.util.Collections.emptyList;
 
-/**
- * @author Hans Dockter
- */
 public class GUtil {
     private static final Pattern WORD_SEPARATOR = Pattern.compile("\\W+");
     private static final Pattern UPPER_LOWER = Pattern.compile("(\\p{Upper}*)(\\p{Lower}*)");
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 674eb14..c87eb2c 100644
--- a/subprojects/core/src/main/groovy/org/gradle/util/GradleVersion.java
+++ b/subprojects/core/src/main/groovy/org/gradle/util/GradleVersion.java
@@ -20,7 +20,6 @@ import groovy.lang.GroovySystem;
 import org.apache.ivy.Ivy;
 import org.apache.tools.ant.Main;
 import org.gradle.api.GradleException;
-import org.gradle.api.Nullable;
 import org.gradle.api.UncheckedIOException;
 import org.gradle.internal.UncheckedException;
 import org.gradle.internal.jvm.Jvm;
@@ -39,16 +38,16 @@ import java.util.TimeZone;
 import java.util.regex.Matcher;
 import java.util.regex.Pattern;
 
-/**
- * @author Hans Dockter
- * @author Russel Winder
- */
 public class GradleVersion implements Comparable<GradleVersion> {
-    public final static String URL = "http://www.gradle.org";
-    private final static Pattern VERSION_PATTERN = Pattern.compile("(\\d+(\\.\\d+)+)(-(\\p{Alpha}+)-(\\d+[a-z]?))?(-(\\d{14}([-+]\\d{4})?))?");
+    public static final String URL = "http://www.gradle.org";
+    private static final Pattern VERSION_PATTERN = Pattern.compile("((\\d+)(\\.\\d+)+)(-(\\p{Alpha}+)-(\\d+[a-z]?))?(-(\\d{14}([-+]\\d{4})?))?");
+    private static final int STAGE_MILESTONE = 0;
 
     private final String version;
+    private final int majorPart;
     private final String buildTime;
+    private final String commitId;
+    private final String buildNumber;
     private final Long snapshot;
     private final String versionPart;
     private final Stage stage;
@@ -69,9 +68,11 @@ public class GradleVersion implements Comparable<GradleVersion> {
 
             String version = properties.get("versionNumber").toString();
             String buildTimestamp = properties.get("buildTimestamp").toString();
+            String buildNumber = properties.get("buildNumber").toString();
+            String commitId = properties.get("commitId").toString();
             Date buildTime = new SimpleDateFormat("yyyyMMddHHmmssZ").parse(buildTimestamp);
 
-            CURRENT = new GradleVersion(version, buildTime);
+            CURRENT = new GradleVersion(version, buildTime, buildNumber, commitId);
         } catch (Exception e) {
             throw new GradleException(String.format("Could not load version details from resource '%s'.", resource), e);
         } finally {
@@ -85,51 +86,58 @@ public class GradleVersion implements Comparable<GradleVersion> {
         }
     }
 
+
     public static GradleVersion current() {
         return CURRENT;
     }
 
-    public static GradleVersion version(String version) {
-        return new GradleVersion(version, null);
+    /**
+     * Parses the given string into a GradleVersion.
+     *
+     * @throws IllegalArgumentException On unrecognized version string.
+     */
+    public static GradleVersion version(String version) throws IllegalArgumentException {
+        return new GradleVersion(version, null, null, null);
     }
 
-    private GradleVersion(String version, Date buildTime) {
+    private GradleVersion(String version, Date buildTime, String buildNumber, String commitId) {
         this.version = version;
+        this.buildNumber = buildNumber;
+        this.commitId = commitId;
         this.buildTime = buildTime == null ? null : formatBuildTime(buildTime);
         Matcher matcher = VERSION_PATTERN.matcher(version);
         if (!matcher.matches()) {
-            // Unrecognized version
-            versionPart = null;
-            snapshot = null;
-            stage = null;
-            return;
+            throw new IllegalArgumentException(String.format("'%s' is not a valid Gradle version string (examples: '1.0', '1.0-rc-1')", version));
         }
 
         versionPart = matcher.group(1);
+        majorPart = Integer.parseInt(matcher.group(2), 10);
 
-        if (matcher.group(3) != null) {
-            int stageNumber = 0;
-            if (matcher.group(4).equals("milestone")) {
-                stageNumber = 1;
-            } else if (matcher.group(4).equals("preview")) {
+        if (matcher.group(4) != null) {
+            int stageNumber;
+            if (matcher.group(5).equals("milestone")) {
+                stageNumber = STAGE_MILESTONE;
+            } else if (matcher.group(5).equals("preview")) {
                 stageNumber = 2;
-            } else if (matcher.group(4).equals("rc")) {
+            } else if (matcher.group(5).equals("rc")) {
                 stageNumber = 3;
+            } else {
+                stageNumber = 1;
             }
-            String stageString = matcher.group(5);
+            String stageString = matcher.group(6);
             stage = new Stage(stageNumber, stageString);
         } else {
             stage = null;
         }
 
-        if (matcher.group(7) != null) {
+        if (matcher.group(8) != null) {
             try {
-                if (matcher.group(8) != null) {
-                    snapshot = new SimpleDateFormat("yyyyMMddHHmmssZ").parse(matcher.group(7)).getTime();
+                if (matcher.group(9) != null) {
+                    snapshot = new SimpleDateFormat("yyyyMMddHHmmssZ").parse(matcher.group(8)).getTime();
                 } else {
                     SimpleDateFormat format = new SimpleDateFormat("yyyyMMddHHmmss");
                     format.setTimeZone(TimeZone.getTimeZone("UTC"));
-                    snapshot = format.parse(matcher.group(7)).getTime();
+                    snapshot = format.parse(matcher.group(8)).getTime();
                 }
             } catch (ParseException e) {
                 throw UncheckedException.throwAsUncheckedException(e);
@@ -140,7 +148,7 @@ public class GradleVersion implements Comparable<GradleVersion> {
     }
 
     private String formatBuildTime(Date buildTime) {
-        DateFormat format = DateFormat.getDateTimeInstance(DateFormat.FULL, DateFormat.FULL);
+        DateFormat format = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss z");
         format.setTimeZone(TimeZone.getTimeZone("UTC"));
         return format.format(buildTime);
     }
@@ -159,37 +167,37 @@ public class GradleVersion implements Comparable<GradleVersion> {
     }
 
     public boolean isSnapshot() {
-        return versionPart == null || snapshot != null;
+        return snapshot != null;
     }
 
     /**
-     * The base version number of the overall version.
+     * The base version of this version. For pre-release versions, this is the target version.
      *
      * For example, the version base of '1.2-rc-1' is '1.2'.
      *
-     * @return The version base, or null if the version is unrecognised.
+     * @return The version base
      */
-    @Nullable
-    public String getVersionBase() {
-        return versionPart;
+    public GradleVersion getBaseVersion() {
+        if (stage == null && snapshot == null) {
+            return this;
+        }
+        if (stage != null && stage.stage == STAGE_MILESTONE) {
+            return version(versionPart + "-milestone-" + stage.number);
+        }
+        return version(versionPart);
     }
 
-    public int getMajor() {
-        if (isValid()) {
-            return Integer.valueOf(versionPart.split("\\.", 2)[0], 10);
-        } else {
-            return -1;
+    public GradleVersion getNextMajor() {
+        if (stage != null && stage.stage == STAGE_MILESTONE) {
+            return version(majorPart + ".0");
         }
+        return version((majorPart + 1) + ".0");
     }
 
     public int compareTo(GradleVersion gradleVersion) {
-        assertCanQueryParts();
-        gradleVersion.assertCanQueryParts();
-
         String[] majorVersionParts = versionPart.split("\\.");
         String[] otherMajorVersionParts = gradleVersion.versionPart.split("\\.");
 
-
         for (int i = 0; i < majorVersionParts.length && i < otherMajorVersionParts.length; i++) {
             int part = Integer.parseInt(majorVersionParts[i]);
             int otherPart = Integer.parseInt(otherMajorVersionParts[i]);
@@ -234,12 +242,6 @@ public class GradleVersion implements Comparable<GradleVersion> {
         return 0;
     }
 
-    private void assertCanQueryParts() {
-        if (versionPart == null) {
-            throw new IllegalArgumentException(String.format("Cannot compare unrecognized Gradle version '%s'.", version));
-        }
-    }
-
     @Override
     public boolean equals(Object o) {
         if (o == this) {
@@ -261,17 +263,21 @@ public class GradleVersion implements Comparable<GradleVersion> {
         final StringBuilder sb = new StringBuilder();
         sb.append("\n------------------------------------------------------------\nGradle ");
         sb.append(getVersion());
-        sb.append("\n------------------------------------------------------------\n\nGradle build time: ");
+        sb.append("\n------------------------------------------------------------\n\nBuild time:   ");
         sb.append(getBuildTime());
-        sb.append("\nGroovy: ");
+        sb.append("\nBuild number: ");
+        sb.append(buildNumber);
+        sb.append("\nRevision:     ");
+        sb.append(commitId);
+        sb.append("\n\nGroovy:       ");
         sb.append(GroovySystem.getVersion());
-        sb.append("\nAnt: ");
+        sb.append("\nAnt:          ");
         sb.append(Main.getAntVersion());
-        sb.append("\nIvy: ");
+        sb.append("\nIvy:          ");
         sb.append(Ivy.getIvyVersion());
-        sb.append("\nJVM: ");
+        sb.append("\nJVM:          ");
         sb.append(Jvm.current());
-        sb.append("\nOS: ");
+        sb.append("\nOS:           ");
         sb.append(OperatingSystem.current());
         sb.append("\n");
         return sb.toString();
diff --git a/subprojects/core/src/main/groovy/org/gradle/util/JarUtil.java b/subprojects/core/src/main/groovy/org/gradle/util/JarUtil.java
index 998c81a..51f92a6 100644
--- a/subprojects/core/src/main/groovy/org/gradle/util/JarUtil.java
+++ b/subprojects/core/src/main/groovy/org/gradle/util/JarUtil.java
@@ -19,12 +19,9 @@ package org.gradle.util;
 import org.apache.commons.io.IOUtils;
 
 import java.io.*;
-import java.util.zip.ZipInputStream;
 import java.util.zip.ZipEntry;
+import java.util.zip.ZipInputStream;
 
-/**
- * @author Tom Eyckmans
- */
 public class JarUtil {
     public static boolean extractZipEntry(File jarFile, String entryName, File extractToFile) throws IOException {
         boolean entryExtracted = false;
diff --git a/subprojects/core/src/main/groovy/org/gradle/util/JavaMethod.java b/subprojects/core/src/main/groovy/org/gradle/util/JavaMethod.java
deleted file mode 100644
index bbe0d9e..0000000
--- a/subprojects/core/src/main/groovy/org/gradle/util/JavaMethod.java
+++ /dev/null
@@ -1,75 +0,0 @@
-/*
- * Copyright 2012 the original author or authors.
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- *      http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-package org.gradle.util;
-
-import org.gradle.api.GradleException;
-
-import java.lang.reflect.InvocationTargetException;
-import java.lang.reflect.Method;
-import java.lang.reflect.Modifier;
-import java.util.Arrays;
-
-public class JavaMethod<T, R> {
-    private final Method method;
-    private final Class<R> returnType;
-
-    private JavaMethod(Class<T> target, Class<R> returnType, String name, Class<?>... paramTypes) {
-        this.returnType = returnType;
-        method = findMethod(target, name, paramTypes);
-        method.setAccessible(true);
-    }
-
-    private JavaMethod(Class<T> target, Class<R> returnType, Method method) {
-        this.returnType = returnType;
-        this.method = method;
-        method.setAccessible(true);
-    }
-
-    private Method findMethod(Class target, String name, Class<?>[] paramTypes) {
-        for (Method method : target.getDeclaredMethods()) {
-            if (Modifier.isStatic(method.getModifiers())) {
-                continue;
-            }
-            if (method.getName().equals(name) && Arrays.equals(method.getParameterTypes(), paramTypes)) {
-                return method;
-            }
-        }
-        throw new GradleException(String.format("Could not find method %s(%s) on %s", name, Arrays.toString(paramTypes),
-                target));
-    }
-
-    public R invoke(T target, Object... args) {
-        try {
-            return returnType.cast(method.invoke(target, args));
-        } catch (InvocationTargetException e) {
-            Throwable cause = e.getCause();
-            if (cause instanceof RuntimeException) {
-                throw (RuntimeException) cause;
-            }
-            throw new GradleException(String.format("Could not call %s.%s() on %s", method.getDeclaringClass().getSimpleName(), method.getName(), target), cause);
-        } catch (Exception e) {
-            throw new GradleException(String.format("Could not call %s.%s() on %s", method.getDeclaringClass().getSimpleName(), method.getName(), target), e);
-        }
-    }
-
-    public static <T, R> JavaMethod<T, R> create(Class<T> target, Class<R> returnType, String name, Class<?>... paramTypes) {
-        return new JavaMethod<T, R>(target, returnType, name, paramTypes);
-    }
-
-    public static <T, R> JavaMethod<T, R> create(Class<T> target, Class<R> returnType, Method method) {
-        return new JavaMethod<T, R>(target, returnType, method);
-    }
-}
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 2811d0c..3d1a861 100644
--- a/subprojects/core/src/main/groovy/org/gradle/util/LineBufferingOutputStream.java
+++ b/subprojects/core/src/main/groovy/org/gradle/util/LineBufferingOutputStream.java
@@ -23,8 +23,6 @@ import java.io.OutputStream;
 
 /**
  * An OutputStream which separates bytes written into lines. Uses the platform default encoding. Is not thread safe.
- *
- * @author Hans Dockter
  */
 public class LineBufferingOutputStream extends OutputStream {
     private boolean hasBeenClosed;
diff --git a/subprojects/core/src/main/groovy/org/gradle/util/MultiParentClassLoader.java b/subprojects/core/src/main/groovy/org/gradle/util/MultiParentClassLoader.java
deleted file mode 100644
index d86da52..0000000
--- a/subprojects/core/src/main/groovy/org/gradle/util/MultiParentClassLoader.java
+++ /dev/null
@@ -1,103 +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.util;
-
-import java.util.*;
-import java.net.URL;
-import java.io.IOException;
-import java.util.concurrent.CopyOnWriteArrayList;
-
-/**
- * A {@code ClassLoader} which delegates to multiple parent ClassLoaders.
- */
-public class MultiParentClassLoader extends ClassLoader implements ClasspathSource {
-    private final List<ClassLoader> parents;
-    private final JavaMethod<ClassLoader, Package[]> getPackagesMethod;
-    private final JavaMethod<ClassLoader, Package> getPackageMethod;
-
-    public MultiParentClassLoader(ClassLoader... parents) {
-        super(null);
-        this.parents = new CopyOnWriteArrayList<ClassLoader>(Arrays.asList(parents));
-        getPackagesMethod = JavaMethod.create(ClassLoader.class, Package[].class, "getPackages");
-        getPackageMethod = JavaMethod.create(ClassLoader.class, Package.class, "getPackage", String.class);
-    }
-
-    public void addParent(ClassLoader parent) {
-        parents.add(parent);
-    }
-
-    public void collectClasspath(Collection<? super URL> classpath) {
-        for (ClassLoader parent : parents) {
-            new ClassLoaderBackedClasspathSource(parent).collectClasspath(classpath);
-        }
-    }
-
-    @Override
-    protected Class<?> loadClass(String name, boolean resolve) throws ClassNotFoundException {
-        for (ClassLoader parent : parents) {
-            try {
-                return parent.loadClass(name);
-            } catch (ClassNotFoundException e) {
-                // Expected
-            }
-        }
-        throw new ClassNotFoundException(String.format("%s not found.", name));
-    }
-
-    @Override
-    protected Package getPackage(String name) {
-        for (ClassLoader parent : parents) {
-            Package p = getPackageMethod.invoke(parent, name);
-            if (p != null) {
-                return p;
-            }
-        }
-        return null;
-    }
-
-    @Override
-    protected Package[] getPackages() {
-        Set<Package> packages = new LinkedHashSet<Package>();
-        for (ClassLoader parent : parents) {
-            Package[] parentPackages = getPackagesMethod.invoke(parent);
-            packages.addAll(Arrays.asList(parentPackages));
-        }
-        return packages.toArray(new Package[packages.size()]);
-    }
-
-    @Override
-    public URL getResource(String name) {
-        for (ClassLoader parent : parents) {
-            URL resource = parent.getResource(name);
-            if (resource != null) {
-                return resource;
-            }
-        }
-        return null;
-    }
-
-    @Override
-    public Enumeration<URL> getResources(String name) throws IOException {
-        Set<URL> resources = new LinkedHashSet<URL>();
-        for (ClassLoader parent : parents) {
-            Enumeration<URL> parentResources = parent.getResources(name);
-            while (parentResources.hasMoreElements()) {
-                resources.add(parentResources.nextElement());
-            }
-        }
-        return Collections.enumeration(resources);
-    }
-}
diff --git a/subprojects/core/src/main/groovy/org/gradle/util/MutableURLClassLoader.java b/subprojects/core/src/main/groovy/org/gradle/util/MutableURLClassLoader.java
deleted file mode 100755
index c8a50ce..0000000
--- a/subprojects/core/src/main/groovy/org/gradle/util/MutableURLClassLoader.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.util;
-
-import org.gradle.internal.classpath.ClassPath;
-
-import java.net.URL;
-import java.net.URLClassLoader;
-import java.util.Collection;
-
-public class MutableURLClassLoader extends URLClassLoader {
-    public MutableURLClassLoader(ClassLoader parent, URL... urls) {
-        super(urls, parent);
-    }
-
-    public MutableURLClassLoader(ClassLoader parent, Collection<URL> urls) {
-        super(urls.toArray(new URL[urls.size()]), parent);
-    }
-
-    public MutableURLClassLoader(ClassLoader parent, ClassPath classPath) {
-        super(classPath.getAsURLArray(), parent);
-    }
-
-    @Override
-    public void addURL(URL url) {
-        super.addURL(url);
-    }
-
-    public void addURLs(Iterable<URL> urls) {
-        for (URL url : urls) {
-            addURL(url);
-        }
-    }
-}
diff --git a/subprojects/core/src/main/groovy/org/gradle/util/Path.java b/subprojects/core/src/main/groovy/org/gradle/util/Path.java
index f433fbf..6c70294 100644
--- a/subprojects/core/src/main/groovy/org/gradle/util/Path.java
+++ b/subprojects/core/src/main/groovy/org/gradle/util/Path.java
@@ -23,9 +23,6 @@ import org.gradle.api.Project;
 import java.util.Arrays;
 import java.util.Comparator;
 
-/**
- * @author Hans Dockter
- */
 public class Path implements Comparable<Path> {
     private static final Comparator<String> STRING_COMPARATOR = GUtil.caseInsensitive();
     private final String prefix;
diff --git a/subprojects/core/src/main/groovy/org/gradle/util/ReflectionUtil.groovy b/subprojects/core/src/main/groovy/org/gradle/util/ReflectionUtil.groovy
deleted file mode 100644
index 80a1e05..0000000
--- a/subprojects/core/src/main/groovy/org/gradle/util/ReflectionUtil.groovy
+++ /dev/null
@@ -1,46 +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.util
-
-/**
- * @author Hans Dockter
- */
-class ReflectionUtil {
-    static Object invoke(Object object, String method, Object... params) {
-        object.invokeMethod(method, params)
-    }
-
-    static Object getProperty(Object object, String property) {
-        object."$property"
-    }
-
-    static void setProperty(Object object, String property, Object value) {
-        object."$property" = value
-    }
-
-    static boolean hasProperty(Object object, String property) {
-        object.metaClass.hasProperty(object, property) != null
-    }
-
-    static boolean isClassAvailable(String className) {
-        try {
-            ReflectionUtil.classLoader.loadClass(className)
-            return true
-        } catch (ClassNotFoundException e) {
-            return false
-        }
-    }
-}
\ No newline at end of file
diff --git a/subprojects/core/src/main/groovy/org/gradle/util/SingleMessageLogger.java b/subprojects/core/src/main/groovy/org/gradle/util/SingleMessageLogger.java
index 647345f..cca033d 100644
--- a/subprojects/core/src/main/groovy/org/gradle/util/SingleMessageLogger.java
+++ b/subprojects/core/src/main/groovy/org/gradle/util/SingleMessageLogger.java
@@ -16,12 +16,14 @@
 
 package org.gradle.util;
 
+import net.jcip.annotations.ThreadSafe;
 import org.apache.commons.lang.StringUtils;
-import org.codehaus.groovy.runtime.StackTraceUtils;
-import org.gradle.api.logging.LogLevel;
 import org.gradle.api.logging.Logger;
 import org.gradle.api.logging.Logging;
 import org.gradle.internal.Factory;
+import org.gradle.internal.featurelifecycle.DeprecatedFeatureUsage;
+import org.gradle.internal.featurelifecycle.LoggingDeprecatedFeatureHandler;
+import org.gradle.internal.featurelifecycle.UsageLocationReporter;
 
 import java.util.Collections;
 import java.util.HashSet;
@@ -29,15 +31,11 @@ import java.util.Set;
 import java.util.concurrent.locks.Lock;
 import java.util.concurrent.locks.ReentrantLock;
 
+ at ThreadSafe
 public class SingleMessageLogger {
-
     private static final Logger LOGGER = Logging.getLogger(DeprecationLogger.class);
-    private static final Set<String> PLUGINS = Collections.synchronizedSet(new HashSet<String>());
-    private static final Set<String> TASKS = Collections.synchronizedSet(new HashSet<String>());
-    private static final Set<String> METHODS = Collections.synchronizedSet(new HashSet<String>());
     private static final Set<String> DYNAMIC_PROPERTIES = Collections.synchronizedSet(new HashSet<String>());
-    private static final Set<String> PROPERTIES = Collections.synchronizedSet(new HashSet<String>());
-    private static final Set<String> NAMED_PARAMETERS = Collections.synchronizedSet(new HashSet<String>());
+    private static final Set<String> FEATURES = Collections.synchronizedSet(new HashSet<String>());
 
     private static final ThreadLocal<Boolean> ENABLED = new ThreadLocal<Boolean>() {
         @Override
@@ -46,128 +44,106 @@ public class SingleMessageLogger {
         }
     };
 
-    private static final ThreadLocal<Boolean> LOG_TRACE = new ThreadLocal<Boolean>() {
-        @Override
-        protected Boolean initialValue() {
-            return false;
-        }
-    };
-
     public static final String ORG_GRADLE_DEPRECATION_TRACE_PROPERTY_NAME = "org.gradle.deprecation.trace";
+    public static final String INCUBATION_MESSAGE = "%s is an incubating feature.";
 
+    private static final Lock LOCK = new ReentrantLock();
+    private static LoggingDeprecatedFeatureHandler handler = new LoggingDeprecatedFeatureHandler();
     private static String deprecationMessage;
-    private static Lock deprecationMessageLock = new ReentrantLock();
-    public static final String INCUBATION_MESSAGE = "%s is an incubating feature. Enjoy it and let us know how it works for you.";
 
     private static String getDeprecationMessage() {
-        if (deprecationMessage == null) {
-            deprecationMessageLock.lock();
-            try {
-                if (deprecationMessage == null) {
-                    String messageBase = "has been deprecated and is scheduled to be removed in";
-                    String when;
-
-                    GradleVersion currentVersion = GradleVersion.current();
-                    int versionMajor = currentVersion.getMajor();
-                    if (versionMajor == -1) { // don't understand version number
-                        when = "the next major version of Gradle";
-                    } else {
-                        when = String.format("Gradle %d.0", versionMajor + 1);
-                    }
-
-                    deprecationMessage = String.format("%s %s", messageBase, when);
-                }
-            } finally {
-                deprecationMessageLock.unlock();
+        LOCK.lock();
+        try {
+            if (deprecationMessage == null) {
+                String messageBase = "has been deprecated and is scheduled to be removed in";
+
+                GradleVersion currentVersion = GradleVersion.current();
+                String when = String.format("Gradle %s", currentVersion.getNextMajor().getVersion());
+
+                deprecationMessage = String.format("%s %s", messageBase, when);
             }
+            return deprecationMessage;
+        } finally {
+            LOCK.unlock();
         }
-
-        return deprecationMessage;
     }
 
     public static void reset() {
-        PLUGINS.clear();
-        METHODS.clear();
-        PROPERTIES.clear();
-        NAMED_PARAMETERS.clear();
         DYNAMIC_PROPERTIES.clear();
+        FEATURES.clear();
+        LOCK.lock();
+        try {
+            handler = new LoggingDeprecatedFeatureHandler();
+        } finally {
+            LOCK.unlock();
+        }
     }
 
-    public static void nagUserOfReplacedPlugin(String pluginName, String replacement) {
-        if (isEnabled() && PLUGINS.add(pluginName)) {
-            LOGGER.warn(String.format(
-                    "The %s plugin %S. Please use the %s plugin instead.",
-                    pluginName, getDeprecationMessage(), replacement));
-            logTraceIfNecessary();
+    public static void useLocationReporter(UsageLocationReporter reporter) {
+        LOCK.lock();
+        try {
+            handler.setLocationReporter(reporter);
+        } finally {
+            LOCK.unlock();
         }
     }
 
+    public static void nagUserOfReplacedPlugin(String pluginName, String replacement) {
+        nagUserWith(String.format(
+                "The %s plugin %s. Please use the %s plugin instead.",
+                pluginName, getDeprecationMessage(), replacement));
+    }
+
     public static void nagUserOfReplacedTaskType(String taskName, String replacement) {
-        if (isEnabled() && TASKS.add(taskName)) {
-            LOGGER.warn(String.format(
-                    "The %s task type %s. Please use the %s instead.",
-                    taskName, getDeprecationMessage(), replacement));
-            logTraceIfNecessary();
-        }
+        nagUserWith(String.format(
+                "The %s task type %s. Please use the %s instead.",
+                taskName, getDeprecationMessage(), replacement));
     }
 
     public static void nagUserOfReplacedMethod(String methodName, String replacement) {
-        if (isEnabled() && METHODS.add(methodName)) {
-            LOGGER.warn(String.format(
-                    "The %s method %s. Please use the %s method instead.",
-                    methodName, getDeprecationMessage(), replacement));
-            logTraceIfNecessary();
-        }
+        nagUserWith(String.format(
+                "The %s method %s. Please use the %s method instead.",
+                methodName, getDeprecationMessage(), replacement));
     }
 
     public static void nagUserOfReplacedProperty(String propertyName, String replacement) {
-        if (isEnabled() && PROPERTIES.add(propertyName)) {
-            LOGGER.warn(String.format(
-                    "The %s property %s. Please use the %s property instead.",
-                    propertyName, getDeprecationMessage(), replacement));
-            logTraceIfNecessary();
-        }
+        nagUserWith(String.format(
+                "The %s property %s. Please use the %s property instead.",
+                propertyName, getDeprecationMessage(), replacement));
     }
 
     public static void nagUserOfDiscontinuedMethod(String methodName) {
-        if (isEnabled() && METHODS.add(methodName)) {
-            LOGGER.warn(String.format("The %s method %s.",
-                    methodName, getDeprecationMessage()));
-            logTraceIfNecessary();
-        }
+        nagUserWith(String.format("The %s method %s.",
+                methodName, getDeprecationMessage()));
     }
 
     public static void nagUserOfDiscontinuedProperty(String propertyName, String advice) {
-        if (isEnabled() && PROPERTIES.add(propertyName)) {
-            LOGGER.warn(String.format("The %s property %s. %s",
-                    propertyName, getDeprecationMessage(), advice));
-            logTraceIfNecessary();
-        }
+        nagUserWith(String.format("The %s property %s. %s",
+                propertyName, getDeprecationMessage(), advice));
     }
 
-    public static void nagUserOfReplacedNamedParameter(String parameterName, String replacement) {
-        if (isEnabled() && NAMED_PARAMETERS.add(parameterName)) {
-            LOGGER.warn(String.format(
-                    "The %s named parameter %s. Please use the %s named parameter instead.",
-                    parameterName, getDeprecationMessage(), replacement));
-            logTraceIfNecessary();
-        }
+    public static void nagUserOfDiscontinuedConfiguration(String configurationName, String advice) {
+        nagUserWith(String.format("The %s configuration %s. %s",
+                configurationName, getDeprecationMessage(), advice));
     }
 
-    /**
-     * Try to avoid using this nagging method. The other methods use a consistent wording for when things will be removed.
-     */
-    public static void nagUserWith(String message) {
-        inform(LogLevel.WARN, message);
-        logTraceIfNecessary();
+    public static void nagUserOfReplacedNamedParameter(String parameterName, String replacement) {
+        nagUserWith(String.format(
+                "The %s named parameter %s. Please use the %s named parameter instead.",
+                parameterName, getDeprecationMessage(), replacement));
     }
 
     /**
      * Try to avoid using this nagging method. The other methods use a consistent wording for when things will be removed.
      */
-    public static void inform(LogLevel level, String message) {
-        if (isEnabled() && METHODS.add(message)) {
-            LOGGER.log(level, message);
+    public static void nagUserWith(String message) {
+        if (isEnabled()) {
+            LOCK.lock();
+            try {
+                handler.deprecatedFeatureUsed(new DeprecatedFeatureUsage(message, SingleMessageLogger.class));
+            } finally {
+                LOCK.unlock();
+            }
         }
     }
 
@@ -204,29 +180,10 @@ public class SingleMessageLogger {
         }
     }
 
-    private static boolean isTraceLoggingEnabled() {
-        return Boolean.getBoolean(ORG_GRADLE_DEPRECATION_TRACE_PROPERTY_NAME) || LOG_TRACE.get();
-    }
-
-    private static void logTraceIfNecessary() {
-        if (isTraceLoggingEnabled()) {
-            StackTraceElement[] stack = StackTraceUtils.sanitize(new Exception()).getStackTrace();
-            for (StackTraceElement frame : stack) {
-                if (!frame.getClassName().startsWith(DeprecationLogger.class.getName())) {
-                    LOGGER.warn("    {}", frame.toString());
-                }
-            }
-        }
-    }
-
     private static boolean isEnabled() {
         return ENABLED.get();
     }
 
-    public static void setLogTrace(boolean flag) {
-        LOG_TRACE.set(flag);
-    }
-
     public static void nagUserAboutDynamicProperty(String propertyName, Object target, Object value) {
         if (!isEnabled()) {
             return;
@@ -243,7 +200,9 @@ public class SingleMessageLogger {
         }
     }
 
-    public static void informAboutIncubating(String incubatingFeature) {
-        inform(LogLevel.LIFECYCLE, String.format(INCUBATION_MESSAGE, incubatingFeature));
+    public static void incubatingFeatureUsed(String incubatingFeature) {
+        if (FEATURES.add(incubatingFeature)) {
+            LOGGER.lifecycle(String.format(INCUBATION_MESSAGE, incubatingFeature));
+        }
     }
 }
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 c7f6c9e..90e3186 100644
--- a/subprojects/core/src/main/groovy/org/gradle/util/TextUtil.java
+++ b/subprojects/core/src/main/groovy/org/gradle/util/TextUtil.java
@@ -61,6 +61,13 @@ public class TextUtil {
     }
 
     /**
+     * Converts all line separators in the specified string to a single new line character.
+     */
+    public static String normaliseLineSeparators(String str) {
+        return str == null ? null : convertLineSeparators(str, "\n");
+    }
+
+    /**
      * Converts all native file separators in the specified string to '/'.
      */
     public static String normaliseFileSeparators(String path) {
diff --git a/subprojects/core/src/main/groovy/org/gradle/util/VersionNumber.java b/subprojects/core/src/main/groovy/org/gradle/util/VersionNumber.java
index 3ad64b6..10255ff 100644
--- a/subprojects/core/src/main/groovy/org/gradle/util/VersionNumber.java
+++ b/subprojects/core/src/main/groovy/org/gradle/util/VersionNumber.java
@@ -26,8 +26,13 @@ import java.util.regex.Pattern;
 
 /**
  * Represents, parses, and compares version numbers following a major.minor.micro-qualifier format.
- * Note that this class considers "2.10.0" less than "2.10.0-something", presumably to make it easier to
- * test for "less than any 2.10 version" and "greater than any 2.10 version".
+ * The {@link #parse} method handles missing parts and allows "." to be used instead of "-".
+ *
+ * <p>Note that this class considers "1.2.3-something" less than "1.2.3". Qualifiers are compared
+ * lexicographically ("1.2.3-alpha" < "1.2.3-beta") and case-insensitive ("1.2.3-alpha" < "1.2.3.RELEASE").
+ *
+ * <p>To check if a version number is at least "1.2.3", disregarding a potential qualifier like "beta", use
+ * {@code version.getBaseVersion().compareTo(VersionNumber.parse("1.2.3")) >= 0}.
  */
 public class VersionNumber implements Comparable<VersionNumber> {
     public static final VersionNumber UNKNOWN = new VersionNumber(0, 0, 0, null);
@@ -63,11 +68,15 @@ public class VersionNumber implements Comparable<VersionNumber> {
         return qualifier;
     }
 
+    public VersionNumber getBaseVersion() {
+        return new VersionNumber(major, minor, micro, null);
+    }
+
     public int compareTo(VersionNumber other) {
         if (major != other.major) { return major - other.major; }
         if (minor != other.minor) { return minor - other.minor; }
         if (micro != other.micro) { return micro - other.micro; }
-        return Ordering.natural().nullsFirst().compare(qualifier, other.qualifier);
+        return Ordering.natural().nullsLast().compare(toLowerCase(qualifier), toLowerCase(other.qualifier));
     }
 
     public boolean equals(Object other) {
@@ -100,5 +109,9 @@ public class VersionNumber implements Comparable<VersionNumber> {
 
         return new VersionNumber(major, minor, micro, qualifier);
     }
+
+    private String toLowerCase(@Nullable String string) {
+        return string == null ? null : string.toLowerCase();
+    }
 }
 
diff --git a/subprojects/core/src/main/groovy/org/gradle/util/internal/LimitedDescription.java b/subprojects/core/src/main/groovy/org/gradle/util/internal/LimitedDescription.java
index 06a7a8a..e629ec0 100644
--- a/subprojects/core/src/main/groovy/org/gradle/util/internal/LimitedDescription.java
+++ b/subprojects/core/src/main/groovy/org/gradle/util/internal/LimitedDescription.java
@@ -23,8 +23,6 @@ import java.util.List;
 
 /**
  * Discards old entries when current count is over the limit.
- * <p>
- * by Szczepan Faber, created at: 2/28/12
  */
 public class LimitedDescription {
 
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
deleted file mode 100644
index 1d22590..0000000
--- a/subprojects/core/src/main/resources/org/gradle/configuration/default-imports.txt
+++ /dev/null
@@ -1,38 +0,0 @@
-import org.gradle.*
-import org.gradle.util.*
-import org.gradle.api.*
-import org.gradle.api.artifacts.*
-import org.gradle.api.artifacts.result.*
-import org.gradle.api.artifacts.dsl.*
-import org.gradle.api.artifacts.maven.*
-import org.gradle.api.artifacts.specs.*
-import org.gradle.api.publish.*
-import org.gradle.api.publish.ivy.*
-import org.gradle.api.publish.ivy.tasks.*
-import org.gradle.api.publish.maven.*
-import org.gradle.api.publish.maven.tasks.*
-import org.gradle.api.execution.*
-import org.gradle.api.file.*
-import org.gradle.api.resources.*
-import org.gradle.api.initialization.*
-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.plugins.jetty.*
-import org.gradle.api.plugins.quality.*
-import org.gradle.api.plugins.announce.*
-import org.gradle.api.plugins.buildcomparison.gradle.*
-import org.gradle.api.specs.*
-import org.gradle.api.tasks.*
-import org.gradle.api.tasks.bundling.*
-import org.gradle.api.tasks.diagnostics.*
-import org.gradle.api.tasks.compile.*
-import org.gradle.api.tasks.javadoc.*
-import org.gradle.api.tasks.testing.*
-import org.gradle.api.tasks.util.*
-import org.gradle.api.tasks.wrapper.*
-import org.gradle.api.tasks.scala.*
-import org.gradle.process.*
diff --git a/subprojects/core/src/main/resources/org/gradle/initialization/buildsrc/defaultBuildSourceScript.txt b/subprojects/core/src/main/resources/org/gradle/initialization/buildsrc/defaultBuildSourceScript.txt
new file mode 100644
index 0000000..86b3740
--- /dev/null
+++ b/subprojects/core/src/main/resources/org/gradle/initialization/buildsrc/defaultBuildSourceScript.txt
@@ -0,0 +1,6 @@
+apply plugin: 'groovy'
+dependencies {
+    compile gradleApi()
+    compile localGroovy()
+}
+
diff --git a/subprojects/core/src/main/resources/org/gradle/initialization/defaultBuildSourceScript.txt b/subprojects/core/src/main/resources/org/gradle/initialization/defaultBuildSourceScript.txt
deleted file mode 100644
index a47ee21..0000000
--- a/subprojects/core/src/main/resources/org/gradle/initialization/defaultBuildSourceScript.txt
+++ /dev/null
@@ -1,5 +0,0 @@
-apply plugin: 'groovy'
-dependencies {
-    compile gradleApi()
-    groovy localGroovy()
-}
diff --git a/subprojects/core/src/test/groovy/org/gradle/StartParameterTest.groovy b/subprojects/core/src/test/groovy/org/gradle/StartParameterTest.groovy
index fccd6e5..2b4ade3 100644
--- a/subprojects/core/src/test/groovy/org/gradle/StartParameterTest.groovy
+++ b/subprojects/core/src/test/groovy/org/gradle/StartParameterTest.groovy
@@ -25,9 +25,6 @@ import spock.lang.Specification
 import static org.gradle.util.Matchers.isSerializable
 import static org.junit.Assert.assertThat
 
-/**
- * @author Hans Dockter
- */
 class StartParameterTest extends Specification {
     @Rule private TestNameTestDirectoryProvider tmpDir = new TestNameTestDirectoryProvider()
     @Rule private SetSystemProperties systemProperties = new SetSystemProperties()
@@ -286,19 +283,6 @@ class StartParameterTest extends Specification {
         assertThat(newParameter, isSerializable())
     }
     
-    void "system properties are merged"() {
-        def parameter = new StartParameter()
-
-        System.properties.clear()
-        System.properties.a = "sys a"
-        System.properties.c = "sys c"
-
-        parameter.systemPropertiesArgs= [a: 'a', b: 'b']
-
-        expect:
-        parameter.mergedSystemProperties.sort() == [a: 'a', b: 'b', c: 'sys c']
-    }
-
     void "gets all init scripts"() {
         def gradleUserHomeDir = tmpDir.testDirectory.createDir("gradleUserHomeDie")
         def gradleHomeDir = tmpDir.testDirectory.createDir("gradleHomeDir")
@@ -330,16 +314,4 @@ class StartParameterTest extends Specification {
         then:
         parameter.allInitScripts == [userMainInit, userInit1, userInit2, distroInit1, distroInit2]
     }
-
-    def "knows if parallel feature was configured"() {
-        def parameter = new StartParameter()
-        assert !parameter.parallelThreadCountConfigured
-
-        when:
-        parameter.setParallelThreadCount(15)
-
-        then:
-        parameter.parallelThreadCount == 15
-        parameter.parallelThreadCountConfigured
-    }
 }
diff --git a/subprojects/core/src/test/groovy/org/gradle/api/AllGradleExceptionsTest.groovy b/subprojects/core/src/test/groovy/org/gradle/api/AllGradleExceptionsTest.groovy
index 4523490..47a1f9c 100644
--- a/subprojects/core/src/test/groovy/org/gradle/api/AllGradleExceptionsTest.groovy
+++ b/subprojects/core/src/test/groovy/org/gradle/api/AllGradleExceptionsTest.groovy
@@ -17,11 +17,9 @@
 package org.gradle.api
 
 import org.junit.Test
-import static org.junit.Assert.*;
 
-/**
- * @author Hans Dockter
- */
+import static org.junit.Assert.assertEquals;
+
 class AllGradleExceptionsTest {
     static final List EXCEPTION_CLASSES = [UnknownTaskException, UnknownProjectException, InvalidUserDataException, GradleException, CircularReferenceException]
 
diff --git a/subprojects/core/src/test/groovy/org/gradle/api/file/ProjectCopySpecTest.groovy b/subprojects/core/src/test/groovy/org/gradle/api/file/ProjectCopySpecTest.groovy
new file mode 100644
index 0000000..ec124a5
--- /dev/null
+++ b/subprojects/core/src/test/groovy/org/gradle/api/file/ProjectCopySpecTest.groovy
@@ -0,0 +1,104 @@
+/*
+ * Copyright 2013 the original author or authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF 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.Project
+import org.gradle.test.fixtures.file.TestFile
+import org.gradle.test.fixtures.file.TestNameTestDirectoryProvider
+import org.gradle.testfixtures.ProjectBuilder
+import org.junit.Rule
+import spock.lang.Specification
+
+class ProjectCopySpecTest extends Specification {
+
+    Project project
+
+    @Rule TestNameTestDirectoryProvider testDirectoryProvider = new TestNameTestDirectoryProvider()
+
+    def setup() {
+        project = ProjectBuilder.builder().withProjectDir(testDirectoryProvider.testDirectory).build()
+    }
+
+    TestFile getCopySource() {
+        testDirectoryProvider.testDirectory.createDir("source")
+    }
+
+    TestFile getCopyDest() {
+        testDirectoryProvider.testDirectory.createDir("dest")
+    }
+
+
+    def "copy spec is enhanced"() {
+        given:
+        def copySpecRootCalled = false
+        def copySpecEachFileCalled = false
+        def copySpecNestedEachFileCalled = false
+        def copyRootCalled = false
+        def copyEachFileCalled = false
+        def copyNestedEachFileCalled = false
+
+        copySource.createFile("file")
+        def copySpec = project.copySpec {
+            copySpecRootCalled = true
+            delegate.duplicatesStrategy "include"
+            from copySource
+
+            from copySource, {
+                delegate.duplicatesStrategy "include"
+                delegate.eachFile {
+                    copySpecNestedEachFileCalled = true
+                    delegate.duplicatesStrategy "include"
+                }
+            }
+
+            eachFile {
+                copySpecEachFileCalled = true
+                delegate.duplicatesStrategy "include"
+            }
+        }
+
+        expect:
+        project.copy {
+            copyRootCalled = true
+            into copyDest
+            with copySpec
+            from copySource
+            from copySource, {
+                delegate.duplicatesStrategy "include"
+                eachFile {
+                    copyNestedEachFileCalled = true
+                    delegate.duplicatesStrategy "include"
+                }
+            }
+            delegate.duplicatesStrategy "include"
+
+            eachFile {
+                delegate.duplicatesStrategy "include"
+                copyEachFileCalled = true
+            }
+        }
+
+        and:
+        copyRootCalled
+        copyEachFileCalled
+        copyNestedEachFileCalled
+        copySpecRootCalled
+        copySpecEachFileCalled
+        copySpecNestedEachFileCalled
+    }
+
+}
diff --git a/subprojects/core/src/test/groovy/org/gradle/api/internal/AbstractNamedDomainObjectContainerTest.groovy b/subprojects/core/src/test/groovy/org/gradle/api/internal/AbstractNamedDomainObjectContainerTest.groovy
index 75d6cf1..7d6d560 100644
--- a/subprojects/core/src/test/groovy/org/gradle/api/internal/AbstractNamedDomainObjectContainerTest.groovy
+++ b/subprojects/core/src/test/groovy/org/gradle/api/internal/AbstractNamedDomainObjectContainerTest.groovy
@@ -15,115 +15,147 @@
  */
 package org.gradle.api.internal
 
-import org.junit.Ignore
-import org.junit.Test
-import static org.hamcrest.Matchers.*
-import static org.junit.Assert.assertThat
-import static org.junit.Assert.fail
+import org.gradle.api.Action
+import org.gradle.api.InvalidUserDataException
 import org.gradle.internal.reflect.Instantiator
 import org.gradle.internal.reflect.DirectInstantiator
 
-class AbstractNamedDomainObjectContainerTest {
-    private final Instantiator instantiator = new ClassGeneratorBackedInstantiator(new AsmBackedClassGenerator(), new DirectInstantiator())
-    private final AbstractNamedDomainObjectContainer container = instantiator.newInstance(TestContainer.class, instantiator)
+import spock.lang.Ignore
+import spock.lang.Specification
 
-    @Test
-    public void isDynamicObjectAware() {
-        assertThat(container, instanceOf(DynamicObjectAware));
+class AbstractNamedDomainObjectContainerTest extends Specification {
+    Instantiator instantiator = new ClassGeneratorBackedInstantiator(new AsmBackedClassGenerator(), new DirectInstantiator())
+    AbstractNamedDomainObjectContainer container = instantiator.newInstance(TestContainer.class, instantiator)
+
+    def "is dynamic object aware"() {
+        expect:
+        container instanceof DynamicObjectAware
     }
 
-    @Test
-    public void canAddObjectWithName() {
+    def "can create object by name"() {
+        when:
         container.create('obj')
-        assertThat(container.getByName('obj'), equalTo(['obj']))
+
+        then:
+        container.getByName('obj') == ['obj']
     }
 
-    @Test
-    public void canAddAndConfigureAnObjectWithName() {
+    def "can create and configure object using closure"() {
+        when:
         container.create('obj') {
             add(1)
             add('value')
         }
-        assertThat(container.getByName('obj'), equalTo(['obj', 1, 'value']))
+
+        then:
+        container.getByName('obj') == ['obj', 1, 'value']
+    }
+
+    def "can create and configure object using action"() {
+        def action = Mock(Action)
+
+        given:
+        action.execute(_) >> { TestObject obj ->
+            obj.add(1)
+            obj.add('value')
+        }
+
+        when:
+        container.create('obj', action)
+
+        then:
+        container.getByName('obj') == ['obj', 1, 'value']
     }
 
-    @Test
-    public void canUseMaybeCreateToFindOrCreateObjectWithName() {
+    def "can use 'maybeCreate' to find or create object by name"() {
+        when:
         def created = container.maybeCreate('obj')
-        assertThat(container.getByName('obj'), equalTo(['obj']))
 
+        then:
+        container.getByName('obj') == ['obj']
+
+        when:
         def fetched = container.maybeCreate('obj')
-        assertThat(fetched, sameInstance(created))
+
+        then:
+        fetched.is(created)
     }
 
-    @Test
-    public void failsToAddObjectWhenObjectWithSameNameAlreadyInContainer() {
+    def "creation fails if object with same name already exists"() {
         container.create('obj')
 
-        try {
-            container.create('obj')
-            fail()
-        } catch (org.gradle.api.InvalidUserDataException e) {
-            assertThat(e.message, equalTo('Cannot add a TestObject with name \'obj\' as a TestObject with that name already exists.'))
-        }
+        when:
+        container.create('obj')
+
+        then:
+        InvalidUserDataException e = thrown()
+        e.message == 'Cannot add a TestObject with name \'obj\' as a TestObject with that name already exists.'
     }
 
-    @Test
-    public void canConfigureExistingObject() {
+    def "can configure existing object"() {
         container.create('list1')
+
+        when:
         container.configure {
             list1 { add(1) }
         }
-        assertThat(container.list1, equalTo(['list1', 1]))
+
+        then:
+        container.list1 == ['list1', 1]
     }
 
-    @Test
-    public void propagatesNestedMissingMethodException() {
+    def "propagates nested MissingMethodException"() {
         container.create('list1')
-        try {
-            container.configure {
-                list1 { unknown { anotherUnknown(2) } }
-            }
-        } catch (groovy.lang.MissingMethodException e) {
-            assertThat(e.method, equalTo('unknown'))
-            assertThat(e.type, equalTo(TestObject))
+
+        when:
+        container.configure {
+            list1 { unknown { anotherUnknown(2) } }
         }
+
+        then:
+        groovy.lang.MissingMethodException e = thrown()
+        e.method == 'unknown'
+        e.type == TestObject
     }
 
-    @Test
-    public void propagatesMethodInvocationException() {
+    def "propagates method invocation exception"() {
         RuntimeException failure = new RuntimeException()
-        try {
-            container.configure {
-                list1 { throw failure }
-            }
-        } catch (RuntimeException e) {
-            assertThat(e, sameInstance(failure))
+
+        when:
+        container.configure {
+            list1 { throw failure }
         }
+
+        then:
+        RuntimeException e = thrown()
+        e.is(failure)
     }
 
-    @Test
-    public void implicitlyAddsAnObjectWhenContainerIsBeingConfigured() {
+    def "implicitly creates an object when container is being configured"() {
+        when:
         container.configure {
             list1
             list2 { add(1) }
         }
-        assertThat(container.list1, equalTo(['list1']))
-        assertThat(container.list2, equalTo(['list2', 1]))
+
+        then:
+        container.list1 == ['list1']
+        container.list2 == ['list2', 1]
     }
 
-    @Test
-    public void canReferToPropertiesAndMethodsOfOwner() {
+    def "can refer to properties and methods of owner"() {
         new DynamicOwner().configure(container)
-        assertThat(container.asMap.keySet(), equalTo(['list1', 'list2'] as Set))
-        assertThat(container.list1, equalTo(['list1', 'dynamicProp', 'ownerProp', 'ownerMethod', 'dynamicMethod', 'dynamicMethod', 1, 'prop', 'testObjectDynamicMethod']))
-        assertThat(container.list1.prop, equalTo('prop'))
-        assertThat(container.list2, equalTo(['list2', container.list1]))
+
+        expect:
+        container.asMap.keySet() == ['list1', 'list2'] as Set
+        container.list1 == ['list1', 'dynamicProp', 'ownerProp', 'ownerMethod', 'dynamicMethod', 'dynamicMethod', 1, 'prop', 'testObjectDynamicMethod']
+        container.list1.prop == 'prop'
+        container.list2 == ['list2', container.list1]
     }
 
-    @Test @Ignore
-    public void canUseAnItemCalledMainInAScript() {
-        Script script = new GroovyShell().parse("""import org.gradle.util.ConfigureUtil
+    @Ignore
+    def "can use an item called 'main' in a script"() {
+        def script = new GroovyShell().parse("""import org.gradle.util.ConfigureUtil
             c.configure {
                 run
                 main { add(1) }
@@ -131,10 +163,13 @@ class AbstractNamedDomainObjectContainerTest {
 
 """)
         script.getBinding().setProperty("c", container)
+
+        when:
         script.run()
 
-        assertThat(container.run, equalTo(['run']))
-        assertThat(container.main, equalTo(['main', 1]))
+        then:
+        container.run == ['run']
+        container.main == ['main', 1]
     }
 }
 
@@ -195,8 +230,9 @@ class DynamicOwner {
 }
 
 class TestObject extends ArrayList<String> {
-    def String prop
+    String prop
     String name
+
     def methodMissing(String name, Object params) {
         if (name == 'testObjectDynamicMethod') {
             add(name)
diff --git a/subprojects/core/src/test/groovy/org/gradle/api/internal/AbstractTaskSpec.groovy b/subprojects/core/src/test/groovy/org/gradle/api/internal/AbstractTaskSpec.groovy
index d7607f0..c8d5ae1 100644
--- a/subprojects/core/src/test/groovy/org/gradle/api/internal/AbstractTaskSpec.groovy
+++ b/subprojects/core/src/test/groovy/org/gradle/api/internal/AbstractTaskSpec.groovy
@@ -25,7 +25,7 @@ import org.gradle.internal.reflect.DirectInstantiator
 import org.gradle.internal.reflect.Instantiator
 import org.gradle.internal.service.DefaultServiceRegistry
 import org.gradle.util.GUtil
-import org.gradle.util.HelperUtil
+import org.gradle.util.TestUtil
 import org.junit.Test
 import spock.lang.Specification
 
@@ -42,7 +42,7 @@ class AbstractTaskSpec extends Specification {
     }
 
     public Task createTask(String name) {
-        AbstractProject project = HelperUtil.createRootProject();
+        AbstractProject project = TestUtil.createRootProject();
         DefaultServiceRegistry registry = new DefaultServiceRegistry();
         registry.add(Instantiator.class, new DirectInstantiator());
         Task task = rootFactory.createChild(project, instantiator).createTask(GUtil.map(Task.TASK_TYPE, TestTask.class, Task.TASK_NAME, name));
diff --git a/subprojects/core/src/test/groovy/org/gradle/api/internal/AsmBackedClassGeneratorGroovyTest.groovy b/subprojects/core/src/test/groovy/org/gradle/api/internal/AsmBackedClassGeneratorGroovyTest.groovy
index 5523da1..69f3cef 100644
--- a/subprojects/core/src/test/groovy/org/gradle/api/internal/AsmBackedClassGeneratorGroovyTest.groovy
+++ b/subprojects/core/src/test/groovy/org/gradle/api/internal/AsmBackedClassGeneratorGroovyTest.groovy
@@ -16,11 +16,14 @@
 
 package org.gradle.api.internal
 
+import org.gradle.api.Action
+import org.gradle.api.NonExtensible
+import org.gradle.api.internal.coerce.TypeCoercionException
+import org.gradle.api.plugins.ExtensionAware
 import org.gradle.internal.reflect.DirectInstantiator
-import spock.lang.Specification
-import spock.lang.Issue
 import org.gradle.util.ConfigureUtil
-import org.gradle.api.Action
+import spock.lang.Issue
+import spock.lang.Specification
 
 class AsmBackedClassGeneratorGroovyTest extends Specification {
 
@@ -138,9 +141,164 @@ class AsmBackedClassGeneratorGroovyTest extends Specification {
         tester.lastArgs.last().is(closure)
     }
 
+    def "can coerce enum values"() {
+        given:
+        def i = create(EnumCoerceTestSubject)
+
+        when:
+        i.enumProperty = "abc"
+
+        then:
+        i.enumProperty == TestEnum.ABC
+
+        when:
+        i.someEnumMethod("DEF")
+
+        then:
+        i.enumProperty == TestEnum.DEF
+
+        when:
+        i.enumProperty "abc"
+
+        then:
+        i.enumProperty == TestEnum.ABC
+
+        when:
+        i.enumProperty "foo"
+
+        then:
+        thrown TypeCoercionException
+
+        when:
+        i.enumMethodWithStringOverload("foo")
+
+        then:
+        i.stringValue == "foo"
+
+        when:
+        i.enumMethodWithStringOverload(TestEnum.DEF)
+
+        then:
+        i.enumProperty == TestEnum.DEF
+    }
+
+    def "can call methods during construction"() {
+        /*
+            We route all methods through invokeMethod, which requires fields
+            added in the subclass. We have special handling for the case where
+            methods are called before this field has been initialised; this tests that.
+         */
+        when:
+        def i = create(CallsMethodDuringConstruction)
+
+        then:
+        i.setDuringConstructor == i.class
+        i.setAtFieldInit == i.class
+    }
+
+    def "can call private methods internally"() {
+        /*
+            We have to specially handle private methods in our dynamic protocol.
+         */
+        given:
+        def i = create(CallsPrivateMethods)
+
+        when:
+        i.flagCalled("a")
+
+        then:
+        i.calledWith == String
+
+        when:
+        i.flagCalled(1.2)
+
+        then:
+        i.calledWith == Number
+
+        when:
+        i.flagCalled([])
+
+        then:
+        i.calledWith == Object
+
+        when:
+        i.flagCalled(1)
+
+        then:
+        i.calledWith == Integer
+    }
+
+
+    def "can use non extensible objects"() {
+        def i = create(NonExtensibleObject)
+
+        when:
+        i.testEnum "ABC"
+
+        then:
+        i.testEnum == TestEnum.ABC
+
+        !(TestEnum instanceof ExtensionAware)
+        !(TestEnum instanceof IConventionAware)
+        !(TestEnum instanceof HasConvention)
+
+        when:
+        i.ext.foo = "bar"
+
+        then:
+        def e = thrown(MissingPropertyException)
+        e.property == "ext"
+    }
+
     def conf(o, c) {
         ConfigureUtil.configure(c, o)
     }
+
+    @Issue("http://issues.gradle.org/browse/GRADLE-2863")
+    def "checked exceptions from private methods are thrown"() {
+            when:
+        create(CallsPrivateMethods).callsPrivateThatThrowsCheckedException("1")
+
+        then:
+        thrown IOException
+    }
+
+    @Issue("http://issues.gradle.org/browse/GRADLE-2863")
+    def "private methods are called with Groovy semantics"() {
+        when:
+        def foo = "bar"
+        def obj = create(CallsPrivateMethods)
+
+        then:
+        obj.callsPrivateStringMethodWithGString("$foo") == "BAR"
+    }
+}
+
+enum TestEnum {
+    ABC, DEF
+}
+
+class EnumCoerceTestSubject {
+    TestEnum enumProperty
+
+    String stringValue
+
+    void someEnumMethod(TestEnum testEnum) {
+        this.enumProperty = testEnum
+    }
+
+    void enumMethodWithStringOverload(TestEnum testEnum) {
+        enumProperty = testEnum
+    }
+
+    void enumMethodWithStringOverload(String stringValue) {
+        this.stringValue = stringValue
+    }
+}
+
+ at NonExtensible
+class NonExtensibleObject {
+    TestEnum testEnum
 }
 
 class DynamicThing {
@@ -209,7 +367,62 @@ class ActionsTester {
         lastMethod = "hasClosure"
         lastArgs = [s, closure]
     }
+}
+
+class CallsMethodDuringConstruction {
 
+    Class setAtFieldInit = getClass()
+    Class setDuringConstructor
 
+    CallsMethodDuringConstruction() {
+        setDuringConstructor = getClass()
+    }
+}
 
-}
\ No newline at end of file
+class CallsPrivateMethods {
+
+    Class calledWith
+
+    void flagCalled(arg) {
+        doFlagCalled(arg)
+    }
+
+    private doFlagCalled(String s) {
+        calledWith = String
+    }
+
+    private doFlagCalled(Number s) {
+        calledWith = Number
+    }
+
+    private doFlagCalled(Integer s) {
+        calledWith = Integer
+    }
+
+    private doFlagCalled(Object s) {
+        calledWith = Object
+    }
+
+    // It's important here that we take an untyped arg, and call a method that types a typed arg
+    // See http://issues.gradle.org/browse/GRADLE-2863
+    def callsPrivateThatThrowsCheckedException(s) {
+        try {
+            throwsCheckedException(s)
+        } catch (Exception e) {
+            assert e instanceof IOException
+            throw e
+        }
+    }
+
+    private throwsCheckedException(String a) {
+        throw new IOException("!")
+    }
+
+    def callsPrivateStringMethodWithGString(GString gString) {
+        upperCaser(gString)
+    }
+
+    private upperCaser(String str) {
+        str.toUpperCase()
+    }
+}
diff --git a/subprojects/core/src/test/groovy/org/gradle/api/internal/AsmBackedClassGeneratorTest.java b/subprojects/core/src/test/groovy/org/gradle/api/internal/AsmBackedClassGeneratorTest.java
index 7e86531..0278604 100755
--- a/subprojects/core/src/test/groovy/org/gradle/api/internal/AsmBackedClassGeneratorTest.java
+++ b/subprojects/core/src/test/groovy/org/gradle/api/internal/AsmBackedClassGeneratorTest.java
@@ -35,8 +35,8 @@ import java.lang.reflect.*;
 import java.util.*;
 import java.util.concurrent.Callable;
 
-import static org.gradle.util.HelperUtil.TEST_CLOSURE;
-import static org.gradle.util.HelperUtil.call;
+import static org.gradle.util.TestUtil.TEST_CLOSURE;
+import static org.gradle.util.TestUtil.call;
 import static org.gradle.util.Matchers.isEmpty;
 import static org.gradle.util.WrapUtil.toList;
 import static org.hamcrest.Matchers.*;
diff --git a/subprojects/core/src/test/groovy/org/gradle/api/internal/CachingDirectedGraphWalkerTest.groovy b/subprojects/core/src/test/groovy/org/gradle/api/internal/CachingDirectedGraphWalkerTest.groovy
deleted file mode 100644
index 26da809..0000000
--- a/subprojects/core/src/test/groovy/org/gradle/api/internal/CachingDirectedGraphWalkerTest.groovy
+++ /dev/null
@@ -1,197 +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
-
-import spock.lang.Specification
-
-class CachingDirectedGraphWalkerTest extends Specification {
-    private final DirectedGraphWithEdgeValues<Integer, String> graph = Mock()
-    private final CachingDirectedGraphWalker walker = new CachingDirectedGraphWalker(graph)
-
-    def collectsValuesForASingleNode() {
-        when:
-        walker.add(1)
-        def values = walker.findValues()
-
-        then:
-        1 * graph.getNodeValues(1, _, _) >> { args -> args[1] << '1' }
-        0 * _._
-        values == ['1'] as Set
-    }
-
-    def collectsValuesForEachSuccessorNode() {
-        when:
-        walker.add(1)
-        def values = walker.findValues()
-
-        then:
-        1 * graph.getNodeValues(1, _, _) >> { args -> args[1] << '1'; args[2] << 2; args[2] << 3 }
-        1 * graph.getNodeValues(2, _, _) >> { args -> args[1] << '2'; args[2] << 3 }
-        1 * graph.getNodeValues(3, _, _) >> { args -> args[1] << '3' }
-        3 * graph.getEdgeValues(_, _, _)
-        0 * _._
-        values == ['1', '2', '3'] as Set
-    }
-
-    def collectsValuesForEachEdgeTraversed() {
-        when:
-        walker.add(1)
-        def values = walker.findValues()
-
-        then:
-        1 * graph.getNodeValues(1, _, _) >> { args -> args[2] << 2; args[2] << 3 }
-        1 * graph.getEdgeValues(1, 2, _) >> { args -> args[2] << '1->2' }
-        1 * graph.getEdgeValues(1, 3, _) >> { args -> args[2] << '1->3' }
-        1 * graph.getNodeValues(2, _, _) >> { args -> args[2] << 3 }
-        1 * graph.getEdgeValues(2, 3, _) >> { args -> args[2] << '2->3' }
-        1 * graph.getNodeValues(3, _, _)
-        0 * _._
-        values == ['1->2', '1->3', '2->3'] as Set
-    }
-
-    def collectsValuesForAllStartNodes() {
-        when:
-        walker.add(1, 2)
-        def values = walker.findValues()
-
-        then:
-        1 * graph.getNodeValues(1, _, _) >> { args -> args[1] << '1'; args[2] << 3 }
-        1 * graph.getNodeValues(2, _, _) >> { args -> args[1] << '2'; args[2] << 3 }
-        1 * graph.getNodeValues(3, _, _) >> { args -> args[1] << '3' }
-        2 * graph.getEdgeValues(_, _, _)
-        0 * _._
-        values == ['1', '2', '3'] as Set
-    }
-
-    def collectsValuesWhenCycleIsPresentInGraph() {
-        when:
-        walker.add(1)
-        def values = walker.findValues()
-
-        then:
-        1 * graph.getNodeValues(1, _, _) >> { args -> args[1] << '1'; args[2] << 2 }
-        1 * graph.getEdgeValues(1, 2, _) >> { args -> args[2] << '1->2' }
-        1 * graph.getNodeValues(2, _, _) >> { args -> args[1] << '2'; args[2] << 3 }
-        1 * graph.getEdgeValues(2, 3, _) >> { args -> args[2] << '2->3' }
-        1 * graph.getNodeValues(3, _, _) >> { args -> args[1] << '3'; args[2] << 1 }
-        1 * graph.getEdgeValues(3, 1, _) >> { args -> args[2] << '3->1' }
-        0 * _._
-        values == ['1', '1->2', '2', '2->3', '3', '3->1'] as Set
-    }
-
-    def collectsValuesWhenNodeConnectedToItself() {
-        when:
-        walker.add(1)
-        def values = walker.findValues()
-
-        then:
-        1 * graph.getNodeValues(1, _, _) >> { args -> args[1] << '1'; args[2] << 1 }
-        1 * graph.getEdgeValues(1, 1, _) >> { args -> args[2] << '1->1' }
-        0 * _._
-        values == ['1', '1->1'] as Set
-    }
-
-    def collectsValuesWhenMultipleCyclesInGraph() {
-        when:
-        walker.add(1)
-        def values = walker.findValues()
-
-        then:
-        1 * graph.getNodeValues(1, _, _) >> { args -> args[1] << '1'; args[2] << 1; args[2] << 2 }
-        1 * graph.getNodeValues(2, _, _) >> { args -> args[1] << '2'; args[2] << 3; args[2] << 4 }
-        1 * graph.getNodeValues(3, _, _) >> { args -> args[1] << '3'; args[2] << 2 }
-        1 * graph.getNodeValues(4, _, _) >> { args -> args[1] << '4' }
-        5 * graph.getEdgeValues(_, _, _) >> { args -> args[2] << "${args[0]}->${args[1]}".toString() }
-        0 * _._
-        values == ['1', '1->1', '1->2', '2', '2->3', '2->4', '3', '3->2', '4'] as Set
-    }
-
-    def canReuseWalkerForMultipleSearches() {
-        when:
-        walker.add(1)
-        def values = walker.findValues()
-
-        then:
-        1 * graph.getNodeValues(1, _, _) >> { args -> args[1] << '1'; args[2] << 2; args[2] << 3 }
-        1 * graph.getNodeValues(2, _, _) >> { args -> args[1] << '2'; args[2] << 3 }
-        1 * graph.getNodeValues(3, _, _) >> { args -> args[1] << '3' }
-        3 * graph.getEdgeValues(_, _, _)
-        0 * _._
-        values == ['1', '2', '3'] as Set
-
-        // Cached node (1) is reachable via 2 separate new paths (4->5->1 and 4->6->1)
-        when:
-        walker.add(4)
-        values = walker.findValues()
-
-        then:
-        1 * graph.getNodeValues(4, _, _) >> { args -> args[1] << '4'; args[2] << 5; args[2] << 6 }
-        1 * graph.getNodeValues(5, _, _) >> { args -> args[1] << '5'; args[2] << 1 }
-        1 * graph.getNodeValues(6, _, _) >> { args -> args[1] << '6'; args[2] << 1 }
-        4 * graph.getEdgeValues(_, _, _) >> { args -> args[2] << "${args[0]}->${args[1]}".toString() }
-        0 * _._
-        values == ['4', '4->5', '4->6', '5', '5->1', '6', '6->1', '1', '2', '3'] as Set
-
-        when:
-        walker.add(2)
-        values = walker.findValues()
-
-        then:
-        values == ['2', '3'] as Set
-    }
-
-    def canReuseWalkerWhenGraphContainsACycle() {
-        when:
-        walker.add(1)
-        walker.findValues()
-
-        then:
-        1 * graph.getNodeValues(1, _, _) >> { args -> args[1] << '1'; args[2] << 2 }
-        1 * graph.getNodeValues(2, _, _) >> { args -> args[1] << '2'; args[2] << 3 }
-        1 * graph.getNodeValues(3, _, _) >> { args -> args[1] << '3'; args[2] << 1; args[2] << 4 }
-        1 * graph.getNodeValues(4, _, _) >> { args -> args[1] << '4'; args[2] << 2}
-        5 * graph.getEdgeValues(_, _, _)
-        0 * _._
-
-        when:
-        walker.add(1)
-        def values = walker.findValues()
-
-        then:
-        values == ['1', '2', '3', '4'] as Set
-
-        when:
-        walker.add(2)
-        values = walker.findValues()
-
-        then:
-        values == ['1', '2', '3', '4'] as Set
-
-        when:
-        walker.add(3)
-        values = walker.findValues()
-
-        then:
-        values == ['1', '2', '3', '4'] as Set
-
-        when:
-        walker.add(4)
-        values = walker.findValues()
-
-        then:
-        values == ['1', '2', '3', '4'] as Set
-    }
-}
diff --git a/subprojects/core/src/test/groovy/org/gradle/api/internal/ChainingTransformerTest.java b/subprojects/core/src/test/groovy/org/gradle/api/internal/ChainingTransformerTest.java
index a9c4c5a..1982ce8 100644
--- a/subprojects/core/src/test/groovy/org/gradle/api/internal/ChainingTransformerTest.java
+++ b/subprojects/core/src/test/groovy/org/gradle/api/internal/ChainingTransformerTest.java
@@ -15,6 +15,7 @@
  */
 package org.gradle.api.internal;
 
+import org.gradle.util.TestUtil;
 import org.jmock.integration.junit4.JUnit4Mockery;
 import org.jmock.integration.junit4.JMock;
 import org.jmock.Expectations;
@@ -22,7 +23,6 @@ import static org.hamcrest.Matchers.*;
 import static org.junit.Assert.*;
 import org.junit.Test;
 import org.junit.runner.RunWith;
-import org.gradle.util.HelperUtil;
 import org.gradle.api.Transformer;
 import groovy.lang.Closure;
 
@@ -59,7 +59,7 @@ public class ChainingTransformerTest {
 
     @Test
     public void canUseAClosureAsATransformer() {
-        Closure closure = HelperUtil.toClosure("{ it + ' transformed' }");
+        Closure closure = TestUtil.toClosure("{ it + ' transformed' }");
 
         transformer.add(closure);
 
@@ -68,7 +68,7 @@ public class ChainingTransformerTest {
 
     @Test
     public void usesOriginalObjectWhenClosureReturnsNull() {
-        Closure closure = HelperUtil.toClosure("{ null }");
+        Closure closure = TestUtil.toClosure("{ null }");
 
         transformer.add(closure);
 
@@ -77,7 +77,7 @@ public class ChainingTransformerTest {
 
     @Test
     public void usesOriginalObjectWhenClosureReturnsObjectOfUnexpectedType() {
-        Closure closure = HelperUtil.toClosure("{ 9 }");
+        Closure closure = TestUtil.toClosure("{ 9 }");
 
         transformer.add(closure);
 
@@ -86,7 +86,7 @@ public class ChainingTransformerTest {
 
     @Test
     public void originalObjectIsSetAsDelegateForClosure() {
-        Closure closure = HelperUtil.toClosure("{ substring(1, 3) }");
+        Closure closure = TestUtil.toClosure("{ substring(1, 3) }");
 
         transformer.add(closure);
 
@@ -95,7 +95,7 @@ public class ChainingTransformerTest {
     
     @Test
     public void closureCanTransformAStringIntoAGString() {
-        Closure closure = HelperUtil.toClosure("{ \"[$it]\" }");
+        Closure closure = TestUtil.toClosure("{ \"[$it]\" }");
 
         transformer.add(closure);
 
diff --git a/subprojects/core/src/test/groovy/org/gradle/api/internal/CompositeDomainObjectSetTest.groovy b/subprojects/core/src/test/groovy/org/gradle/api/internal/CompositeDomainObjectSetTest.groovy
index 379a394..1a8a981 100644
--- a/subprojects/core/src/test/groovy/org/gradle/api/internal/CompositeDomainObjectSetTest.groovy
+++ b/subprojects/core/src/test/groovy/org/gradle/api/internal/CompositeDomainObjectSetTest.groovy
@@ -345,13 +345,12 @@ class CompositeDomainObjectSetTest extends Specification {
 
         and:
         component1 << "a" << "c"
-        component2 << "a" << "d"
+        component2 << "a" << "d" << "a"
 
         then:
-        calledFor == ["c", "d"]
+        calledFor == ["a", "c", "d"]
     }
 
-    @IgnoreRest
     def "all notifications are only fired once for each in composite"() {
         given:
         def component1 = collection("a")
@@ -400,4 +399,28 @@ class CompositeDomainObjectSetTest extends Specification {
         thrown UnsupportedOperationException
     }
 
+    def "behaves when the same collection added"() {
+        def same = collection("a", "b")
+        def composite = composite(same, same, same)
+
+        expect:
+        composite.toList() == ['a', 'b']
+
+        when:
+        same << 'c'
+
+        then:
+        composite.toList() == ['a', 'b', 'c']
+    }
+
+    def "removing collection removes all instances"() {
+        def instance = collection("a", "b")
+        def composite = composite(instance, instance)
+
+        when:
+        composite.removeCollection(instance)
+
+        then:
+        composite.toList() == []
+    }
 }
\ No newline at end of file
diff --git a/subprojects/core/src/test/groovy/org/gradle/api/internal/ConventionAwareHelperTest.java b/subprojects/core/src/test/groovy/org/gradle/api/internal/ConventionAwareHelperTest.java
index 4cd4991..3be760a 100644
--- a/subprojects/core/src/test/groovy/org/gradle/api/internal/ConventionAwareHelperTest.java
+++ b/subprojects/core/src/test/groovy/org/gradle/api/internal/ConventionAwareHelperTest.java
@@ -17,7 +17,7 @@
 package org.gradle.api.internal;
 
 import org.gradle.api.InvalidUserDataException;
-import org.gradle.util.HelperUtil;
+import org.gradle.util.TestUtil;
 import org.gradle.util.TestTask;
 import org.junit.Before;
 import org.junit.Test;
@@ -33,28 +33,25 @@ import static org.hamcrest.Matchers.equalTo;
 import static org.hamcrest.Matchers.sameInstance;
 import static org.junit.Assert.*;
 
-/**
- * @author Hans Dockter
- */
 public class ConventionAwareHelperTest {
     ConventionAwareHelper conventionAware;
 
     TestTask testTask;
 
     @Before public void setUp() {
-        testTask = HelperUtil.createTask(TestTask.class);
+        testTask = TestUtil.createTask(TestTask.class);
         conventionAware = new ConventionAwareHelper(testTask);
     }
 
     @Test
     public void canMapPropertiesUsingClosure() {
-        conventionAware.map("list1", HelperUtil.toClosure("{ ['a'] }"));
+        conventionAware.map("list1", TestUtil.toClosure("{ ['a'] }"));
         assertThat(conventionAware.getConventionValue(null, "list1", false), equalTo((Object) toList("a")));
 
-        conventionAware.map("list1", HelperUtil.toClosure("{ convention -> [convention] }"));
+        conventionAware.map("list1", TestUtil.toClosure("{ convention -> [convention] }"));
         assertThat(conventionAware.getConventionValue(null, "list1", false), equalTo((Object) toList(conventionAware.getConvention())));
 
-        conventionAware.map("list1", HelperUtil.toClosure("{ convention, object -> [convention, object] }"));
+        conventionAware.map("list1", TestUtil.toClosure("{ convention, object -> [convention, object] }"));
         assertThat(conventionAware.getConventionValue(null, "list1", false), equalTo((Object) toList(conventionAware.getConvention(), testTask)));
     }
 
@@ -72,7 +69,7 @@ public class ConventionAwareHelperTest {
     
     @Test
     public void canSetMappingUsingDynamicProperty() {
-        HelperUtil.call("{ it.list1 = { ['a'] } }", conventionAware);
+        TestUtil.call("{ it.list1 = { ['a'] } }", conventionAware);
         assertThat(conventionAware.getConventionValue(null, "list1", false), equalTo((Object) toList("a")));
     }
     
diff --git a/subprojects/core/src/test/groovy/org/gradle/api/internal/DefaultDomainObjectCollectionTest.java b/subprojects/core/src/test/groovy/org/gradle/api/internal/DefaultDomainObjectCollectionTest.java
index 5408f9c..95101d1 100755
--- a/subprojects/core/src/test/groovy/org/gradle/api/internal/DefaultDomainObjectCollectionTest.java
+++ b/subprojects/core/src/test/groovy/org/gradle/api/internal/DefaultDomainObjectCollectionTest.java
@@ -18,7 +18,7 @@ package org.gradle.api.internal;
 import org.gradle.api.Action;
 import org.gradle.api.DomainObjectCollection;
 import org.gradle.api.specs.Spec;
-import org.gradle.util.HelperUtil;
+import org.gradle.util.TestUtil;
 import org.gradle.util.TestClosure;
 import org.hamcrest.Description;
 import org.jmock.Expectations;
@@ -121,7 +121,7 @@ public class DefaultDomainObjectCollectionTest {
         container.add("c");
 
         assertThat(toList(container.matching(spec)), equalTo(toList((CharSequence) "a", "c")));
-        assertThat(toList(container.matching(HelperUtil.toClosure(testClosure))), equalTo(toList((CharSequence) "a", "c")));
+        assertThat(toList(container.matching(TestUtil.toClosure(testClosure))), equalTo(toList((CharSequence) "a", "c")));
     }
 
     @Test
@@ -162,7 +162,7 @@ public class DefaultDomainObjectCollectionTest {
             one(closure).call("a");
         }});
         
-        container.withType(String.class, HelperUtil.toClosure(closure));
+        container.withType(String.class, TestUtil.toClosure(closure));
         container.add("a");
     }
 
@@ -220,7 +220,7 @@ public class DefaultDomainObjectCollectionTest {
             }
         };
 
-        container.matching(spec).whenObjectAdded(HelperUtil.toClosure(closure));
+        container.matching(spec).whenObjectAdded(TestUtil.toClosure(closure));
 
         container.add("a");
         container.add("b");
@@ -254,7 +254,7 @@ public class DefaultDomainObjectCollectionTest {
         container.add("b");
         container.add("c");
 
-        Collection<CharSequence> collection = container.findAll(HelperUtil.toClosure("{ it != 'b' }"));
+        Collection<CharSequence> collection = container.findAll(TestUtil.toClosure("{ it != 'b' }"));
         assertThat(collection, instanceOf(List.class));
         assertThat(collection, equalTo((Collection) toList("a", "c")));
     }
@@ -265,7 +265,7 @@ public class DefaultDomainObjectCollectionTest {
         container.add("b");
         container.add("c");
 
-        Collection<CharSequence> collection = container.findAll(HelperUtil.toClosure("{ it != 'b' }"));
+        Collection<CharSequence> collection = container.findAll(TestUtil.toClosure("{ it != 'b' }"));
 
         container.add("d");
         assertThat(collection, equalTo((Collection) toList("a", "c")));
@@ -292,7 +292,7 @@ public class DefaultDomainObjectCollectionTest {
             one(closure).call("a");
         }});
 
-        container.whenObjectAdded(HelperUtil.toClosure(closure));
+        container.whenObjectAdded(TestUtil.toClosure(closure));
         container.add("a");
     }
 
@@ -306,13 +306,13 @@ public class DefaultDomainObjectCollectionTest {
             one(closure).call("a");
         }});
 
-        container.whenObjectRemoved(HelperUtil.toClosure(closure));
+        container.whenObjectRemoved(TestUtil.toClosure(closure));
         container.remove("a");
     }
 
     @Test
     public void callsClosureWithNewObjectAsDelegateWhenObjectAdded() {
-        container.whenObjectAdded(HelperUtil.toClosure("{ assert delegate == 'a' }"));
+        container.whenObjectAdded(TestUtil.toClosure("{ assert delegate == 'a' }"));
         container.add("a");
     }
 
@@ -373,7 +373,7 @@ public class DefaultDomainObjectCollectionTest {
         }});
 
         container.add("a");
-        container.all(HelperUtil.toClosure(closure));
+        container.all(TestUtil.toClosure(closure));
     }
 
     @Test
@@ -397,13 +397,13 @@ public class DefaultDomainObjectCollectionTest {
             one(closure).call("a");
         }});
 
-        container.all(HelperUtil.toClosure(closure));
+        container.all(TestUtil.toClosure(closure));
         container.add("a");
     }
 
     @Test
     public void allCallsClosureWithObjectAsDelegate() {
-        container.all(HelperUtil.toClosure(" { assert delegate == 'a' } "));
+        container.all(TestUtil.toClosure(" { assert delegate == 'a' } "));
         container.add("a");
     }
 
diff --git a/subprojects/core/src/test/groovy/org/gradle/api/internal/DefaultNamedDomainObjectSetTest.java b/subprojects/core/src/test/groovy/org/gradle/api/internal/DefaultNamedDomainObjectSetTest.java
index 514c216..3ea532d 100644
--- a/subprojects/core/src/test/groovy/org/gradle/api/internal/DefaultNamedDomainObjectSetTest.java
+++ b/subprojects/core/src/test/groovy/org/gradle/api/internal/DefaultNamedDomainObjectSetTest.java
@@ -22,7 +22,7 @@ import org.gradle.api.specs.Spec;
 import org.gradle.api.specs.Specs;
 import org.gradle.util.ConfigureUtil;
 import org.gradle.util.GUtil;
-import org.gradle.util.HelperUtil;
+import org.gradle.util.TestUtil;
 import org.gradle.util.TestClosure;
 import org.jmock.Expectations;
 import org.jmock.integration.junit4.JMock;
@@ -32,8 +32,8 @@ import org.junit.runner.RunWith;
 
 import java.util.Iterator;
 
-import static org.gradle.util.HelperUtil.call;
-import static org.gradle.util.HelperUtil.toClosure;
+import static org.gradle.util.TestUtil.call;
+import static org.gradle.util.TestUtil.toClosure;
 import static org.gradle.util.WrapUtil.toList;
 import static org.hamcrest.Matchers.*;
 import static org.junit.Assert.*;
@@ -167,7 +167,7 @@ public class DefaultNamedDomainObjectSetTest {
         container.add(bean3);
 
         assertThat(toList(container.matching(spec)), equalTo(toList(bean2, bean3)));
-        assertThat(toList(container.matching(HelperUtil.toClosure(testClosure))), equalTo(toList(bean2, bean3)));
+        assertThat(toList(container.matching(TestUtil.toClosure(testClosure))), equalTo(toList(bean2, bean3)));
         assertThat(container.matching(spec).findByName("a"), nullValue());
         assertThat(container.matching(spec).findByName("b"), sameInstance(bean2));
     }
@@ -234,7 +234,7 @@ public class DefaultNamedDomainObjectSetTest {
             one(closure).call(bean2);
         }});
 
-        container.withType(OtherBean.class, HelperUtil.toClosure(closure));
+        container.withType(OtherBean.class, TestUtil.toClosure(closure));
     }
 
     @Test
@@ -306,7 +306,7 @@ public class DefaultNamedDomainObjectSetTest {
             }
         };
 
-        container.matching(spec).whenObjectAdded(HelperUtil.toClosure(closure));
+        container.matching(spec).whenObjectAdded(TestUtil.toClosure(closure));
 
         container.add(bean);
         container.add(new Bean());
@@ -461,7 +461,7 @@ public class DefaultNamedDomainObjectSetTest {
             one(closure).call(bean);
         }});
 
-        container.whenObjectAdded(HelperUtil.toClosure(closure));
+        container.whenObjectAdded(TestUtil.toClosure(closure));
         container.add(bean);
     }
 
@@ -552,7 +552,7 @@ public class DefaultNamedDomainObjectSetTest {
         }});
 
         container.add(bean);
-        container.all(HelperUtil.toClosure(closure));
+        container.all(TestUtil.toClosure(closure));
     }
 
     @Test
@@ -577,7 +577,7 @@ public class DefaultNamedDomainObjectSetTest {
             one(closure).call(bean);
         }});
 
-        container.all(HelperUtil.toClosure(closure));
+        container.all(TestUtil.toClosure(closure));
         container.add(bean);
     }
 
@@ -704,7 +704,7 @@ public class DefaultNamedDomainObjectSetTest {
     public void addRuleByClosure() {
         String testPropertyKey = "org.gradle.test.addRuleByClosure";
         String expectedTaskName = "someTaskName";
-        Closure ruleClosure = HelperUtil.toClosure(String.format("{ taskName -> System.setProperty('%s', '%s') }",
+        Closure ruleClosure = TestUtil.toClosure(String.format("{ taskName -> System.setProperty('%s', '%s') }",
                 testPropertyKey, expectedTaskName));
         container.addRule("description", ruleClosure);
         container.getRules().get(0).apply(expectedTaskName);
diff --git a/subprojects/core/src/test/groovy/org/gradle/api/internal/DefaultPolymorphicDomainObjectContainerBaseTest.groovy b/subprojects/core/src/test/groovy/org/gradle/api/internal/DefaultPolymorphicDomainObjectContainerBaseTest.groovy
new file mode 100644
index 0000000..88954e7
--- /dev/null
+++ b/subprojects/core/src/test/groovy/org/gradle/api/internal/DefaultPolymorphicDomainObjectContainerBaseTest.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.api.internal
+
+import org.gradle.internal.reflect.Instantiator
+
+class DefaultPolymorphicDomainObjectContainerBaseTest extends AbstractNamedDomainObjectContainerTest {
+    def setup() {
+        container = instantiator.newInstance(PolymorphicTestContainer.class, instantiator)
+    }
+
+    static class PolymorphicTestContainer extends AbstractPolymorphicDomainObjectContainer<TestObject> {
+        PolymorphicTestContainer(Instantiator instantiator) {
+            super(TestObject, instantiator, new DynamicPropertyNamer())
+        }
+
+        @Override
+        protected TestObject doCreate(String name) {
+            def testObject = new TestObject()
+            testObject.name = name
+            testObject.add(name)
+            testObject
+        }
+
+        @Override
+        protected <U extends TestObject> U doCreate(String name, Class<U> type) {
+            throw new UnsupportedOperationException("doCreate")
+        }
+    }
+}
diff --git a/subprojects/core/src/test/groovy/org/gradle/api/internal/DefaultPolymorphicDomainObjectContainerDslTest.groovy b/subprojects/core/src/test/groovy/org/gradle/api/internal/DefaultPolymorphicDomainObjectContainerDslTest.groovy
index 5c3b2a5..f097a0d 100644
--- a/subprojects/core/src/test/groovy/org/gradle/api/internal/DefaultPolymorphicDomainObjectContainerDslTest.groovy
+++ b/subprojects/core/src/test/groovy/org/gradle/api/internal/DefaultPolymorphicDomainObjectContainerDslTest.groovy
@@ -18,7 +18,7 @@ package org.gradle.api.internal
 import org.gradle.api.Named
 import org.gradle.api.NamedDomainObjectFactory
 import org.gradle.internal.reflect.Instantiator
-import org.gradle.util.HelperUtil
+import org.gradle.util.TestUtil
 
 import spock.lang.Specification
 
@@ -28,7 +28,7 @@ class DefaultPolymorphicDomainObjectContainerDslTest extends Specification {
     def agedFred = new DefaultAgeAwarePerson(name: "Fred", age: 42)
     def agedBarney = new DefaultAgeAwarePerson(name: "Barney", age: 42)
 
-    def project = HelperUtil.createRootProject()
+    def project = TestUtil.createRootProject()
     def instantiator = project.services.get(Instantiator)
     def container = instantiator.newInstance(DefaultPolymorphicDomainObjectContainer, Person, instantiator)
 
@@ -85,17 +85,13 @@ class DefaultPolymorphicDomainObjectContainerDslTest extends Specification {
         container.asDynamicObject.getProperty("Barney") == barney
     }
 
-    def "configure elements with default type"() {
-
-    }
-
     def "create elements with specified type"() {
         container.registerFactory(Person, { new DefaultPerson(name: it) } as NamedDomainObjectFactory)
         container.registerFactory(AgeAwarePerson, { new DefaultAgeAwarePerson(name: it, age: 42) } as NamedDomainObjectFactory)
 
         when:
         project.container {
-            Fred(Person) {}
+            Fred(Person)
             Barney(AgeAwarePerson) {}
         }
 
@@ -105,6 +101,84 @@ class DefaultPolymorphicDomainObjectContainerDslTest extends Specification {
         container.findByName("Barney") == agedBarney
         container.asDynamicObject.getProperty("Fred") == fred
         container.asDynamicObject.getProperty("Barney") == agedBarney
+    }
+
+    def "configure elements with default type"() {
+        container.registerDefaultFactory({ new DefaultAgeAwarePerson(name: it, age: 42) } as NamedDomainObjectFactory)
+
+        when:
+        project.container {
+            Fred {
+                age = 11
+            }
+            Barney {
+                age = 22
+            }
+        }
 
+        then:
+        container.size() == 2
+        container.findByName("Fred").age == 11
+        container.findByName("Barney").age == 22
+    }
+
+    def "configure elements with specified type"() {
+        container.registerFactory(AgeAwarePerson, { new DefaultAgeAwarePerson(name: it, age: 42) } as NamedDomainObjectFactory)
+
+        when:
+        project.container {
+            Fred(AgeAwarePerson) {
+                age = 11
+            }
+            Barney(AgeAwarePerson) {
+                age = 22
+            }
+        }
+
+        then:
+        container.size() == 2
+        container.findByName("Fred").age == 11
+        container.findByName("Barney").age == 22
+    }
+
+    def "configure same element multiple times"() {
+        container.registerFactory(AgeAwarePerson, { new DefaultAgeAwarePerson(name: it, age: 42) } as NamedDomainObjectFactory)
+
+        when:
+        project.container {
+            Fred(AgeAwarePerson) {
+                age = 11
+            }
+            Barney(AgeAwarePerson) {
+                age = 22
+            }
+            Fred(AgeAwarePerson) {
+                age = 33
+            }
+            Barney(AgeAwarePerson) {
+                age = 44
+            }
+        }
+
+        then:
+        container.size() == 2
+        container.findByName("Fred").age == 33
+        container.findByName("Barney").age == 44
+    }
+
+    def "create elements without configuration"() {
+        container.registerDefaultFactory({ new DefaultAgeAwarePerson(name: it, age: 42) } as NamedDomainObjectFactory)
+        container.registerFactory(AgeAwarePerson, { new DefaultAgeAwarePerson(name: it, age: 43) } as NamedDomainObjectFactory)
+
+        when:
+        project.container {
+            Fred
+            Barney(AgeAwarePerson)
+        }
+
+        then:
+        container.size() == 2
+        container.findByName("Fred").age == 42
+        container.findByName("Barney").age == 43
     }
 }
diff --git a/subprojects/core/src/test/groovy/org/gradle/api/internal/DefaultPolymorphicDomainObjectContainerTest.groovy b/subprojects/core/src/test/groovy/org/gradle/api/internal/DefaultPolymorphicDomainObjectContainerTest.groovy
index 1f1ad01..d149ec2 100644
--- a/subprojects/core/src/test/groovy/org/gradle/api/internal/DefaultPolymorphicDomainObjectContainerTest.groovy
+++ b/subprojects/core/src/test/groovy/org/gradle/api/internal/DefaultPolymorphicDomainObjectContainerTest.groovy
@@ -63,6 +63,18 @@ class DefaultPolymorphicDomainObjectContainerTest extends Specification {
         }
     }
 
+    interface UnnamedPerson {} // doesn't implement Named
+
+    static class DefaultUnnamedPerson {}
+
+    interface CtorNamedPerson extends Person {}
+
+    static class DefaultCtorNamedPerson extends DefaultPerson implements CtorNamedPerson {
+        DefaultCtorNamedPerson(String name) {
+            this.name = name
+        }
+    }
+
     def "add elements"() {
         when:
         container.add(fred)
@@ -95,10 +107,11 @@ class DefaultPolymorphicDomainObjectContainerTest extends Specification {
 
         then:
         InvalidUserDataException e = thrown()
-        e.message == "This container does not support creating domain objects without specifying a type."
+        e.message == "Cannot create a Person named 'fred' because this container does not support creating " +
+                "elements by name alone. Please specify which subtype of Person to create. Known subtypes are: (None)"
     }
 
-    def "create elements with specified type"() {
+    def "create elements with specified type based on NamedDomainObjectFactory"() {
         container.registerFactory(Person, { new DefaultPerson(name: it) } as NamedDomainObjectFactory)
         container.registerFactory(AgeAwarePerson, { new DefaultAgeAwarePerson(name: it, age: 42) } as NamedDomainObjectFactory)
 
@@ -114,6 +127,45 @@ class DefaultPolymorphicDomainObjectContainerTest extends Specification {
         container.asDynamicObject.getProperty("barney") == agedBarney
     }
 
+    def "create elements with specified type based on closure-based factory"() {
+        container.registerFactory(Person, { new DefaultPerson(name: it) })
+        container.registerFactory(AgeAwarePerson, { new DefaultAgeAwarePerson(name: it, age: 42) })
+
+        when:
+        container.create("fred", Person)
+        container.create("barney", AgeAwarePerson)
+
+        then:
+        container.size() == 2
+        container.findByName("fred") == fred
+        container.findByName("barney") == agedBarney
+        container.asDynamicObject.getProperty("fred") == fred
+        container.asDynamicObject.getProperty("barney") == agedBarney
+    }
+
+    def "create elements with specified type based on type binding"() {
+        container = new DefaultPolymorphicDomainObjectContainer<?>(Object, new DirectInstantiator(),
+                { it instanceof Named ? it.name : "unknown" } as Named.Namer)
+
+        container.registerBinding(UnnamedPerson, DefaultUnnamedPerson)
+        container.registerBinding(CtorNamedPerson, DefaultCtorNamedPerson)
+
+        when:
+        container.create("fred", UnnamedPerson)
+        container.create("barney", CtorNamedPerson)
+
+        then:
+        container.size() == 2
+        !container.findByName("fred")
+        with(container.findByName("unknown")) {
+            it.getClass() == DefaultUnnamedPerson
+        }
+        with(container.findByName("barney")) {
+            it.getClass() == DefaultCtorNamedPerson
+            name == "barney"
+        }
+    }
+
     def "throws meaningful exception if it doesn't support creating domain objects with the specified type"() {
         container = new DefaultPolymorphicDomainObjectContainer<Person>(Person, new DirectInstantiator())
 
@@ -122,8 +174,7 @@ class DefaultPolymorphicDomainObjectContainerTest extends Specification {
 
         then:
         InvalidUserDataException e = thrown()
-        e.message == "This container does not support creating domain objects of type " +
-                "'org.gradle.api.internal.DefaultPolymorphicDomainObjectContainerTest\$Person'."
+        e.message == "Cannot create a Person because this type is not known to this container. Known types are: (None)"
     }
 
     def "throws meaningful exception if factory element type is not a subtype of container element type"() {
@@ -132,8 +183,8 @@ class DefaultPolymorphicDomainObjectContainerTest extends Specification {
 
         then:
         IllegalArgumentException e = thrown()
-        e.message == "Factory element type 'java.lang.String' is not a subtype of container element type " +
-                "'org.gradle.api.internal.DefaultPolymorphicDomainObjectContainerTest\$Person'"
+        e.message == "Cannot register a factory for type String because it is not a subtype of " +
+                "container element type Person."
     }
 
     def "fires events when elements are added"() {
diff --git a/subprojects/core/src/test/groovy/org/gradle/api/internal/DefaultTaskTest.groovy b/subprojects/core/src/test/groovy/org/gradle/api/internal/DefaultTaskTest.groovy
index 11be2ae..808b7b6 100644
--- a/subprojects/core/src/test/groovy/org/gradle/api/internal/DefaultTaskTest.groovy
+++ b/subprojects/core/src/test/groovy/org/gradle/api/internal/DefaultTaskTest.groovy
@@ -39,9 +39,6 @@ import static org.gradle.util.Matchers.isEmpty
 import static org.hamcrest.Matchers.*
 import static org.junit.Assert.*
 
-/**
- * @author Hans Dockter
- */
 class DefaultTaskTest extends AbstractTaskTest {
     ClassLoader cl
     DefaultTask defaultTask
@@ -69,7 +66,7 @@ class DefaultTaskTest extends AbstractTaskTest {
     }
 
     @Test public void testHasUsefulToString() {
-        assertEquals('task \':taskname\'', task.toString())
+        assertEquals('task \':testTask\'', task.toString())
     }
 
     @Test public void testCanInjectValuesIntoTaskWhenUsingNoArgsConstructor() {
@@ -92,6 +89,36 @@ class DefaultTaskTest extends AbstractTaskTest {
     }
 
     @Test
+    public void testMustRunAfter() {
+        Task mustRunAfterTask = createTask(project, "mustRunAfter")
+        Task mustRunAfterTaskUsingPath = project.getTasks().add("path")
+        Task task = createTask(project, TEST_TASK_NAME)
+
+        task.mustRunAfter(mustRunAfterTask, "path")
+        assert task.mustRunAfter.getDependencies(task) == [mustRunAfterTask, mustRunAfterTaskUsingPath] as Set
+    }
+
+    @Test
+    public void testFinalizedBy() {
+        Task finalizer = createTask(project, "finalizer")
+        Task finalizerFromPath = project.getTasks().create("path")
+        Task finalized = createTask(project, TEST_TASK_NAME)
+
+        finalized.finalizedBy(finalizer, "path")
+        assert finalized.finalizedBy.getDependencies(finalized) == [finalizer, finalizerFromPath] as Set
+    }
+
+    @Test
+    public void testSetFinalizedBy() {
+        Task finalizer = createTask(project, "finalizer")
+        Task finalizerFromPath = project.getTasks().create("path")
+        Task finalized = createTask(project, TEST_TASK_NAME)
+
+        finalized.finalizedBy = [finalizer, "path"]
+        assert finalized.finalizedBy.getDependencies(finalized) == [finalizer, finalizerFromPath] as Set
+    }
+
+    @Test
     public void testConfigure() {
         Closure action1 = { Task t -> }
         assertSame(task, task.configure {
@@ -239,7 +266,7 @@ class DefaultTaskTest extends AbstractTaskTest {
 
     @Test
     void canGetTemporaryDirectory() {
-        File tmpDir = new File(project.buildDir, "tmp/taskname")
+        File tmpDir = new File(project.buildDir, "tmp/testTask")
         assertFalse(tmpDir.exists())
 
         assertThat(defaultTask.temporaryDir, equalTo(tmpDir))
diff --git a/subprojects/core/src/test/groovy/org/gradle/api/internal/DocumentationRegistryTest.groovy b/subprojects/core/src/test/groovy/org/gradle/api/internal/DocumentationRegistryTest.groovy
index e5b6e70..bb69e62 100644
--- a/subprojects/core/src/test/groovy/org/gradle/api/internal/DocumentationRegistryTest.groovy
+++ b/subprojects/core/src/test/groovy/org/gradle/api/internal/DocumentationRegistryTest.groovy
@@ -23,53 +23,10 @@ import spock.lang.Specification
 
 class DocumentationRegistryTest extends Specification {
     @Rule TestNameTestDirectoryProvider tmpDir
-    final GradleDistributionLocator locator = Mock()
     final GradleVersion gradleVersion = GradleVersion.current()
-    final DocumentationRegistry registry = new DocumentationRegistry(locator, gradleVersion)
-
-
-    def "points users at the local user guide when target page is present in distribution"() {
-        def distDir = tmpDir.createDir("home")
-        distDir.createFile("docs/userguide/userguide.html")
-        def daemonPage = distDir.createFile("docs/userguide/gradle_daemon.html")
-
-        given:
-        _ * locator.gradleHome >> distDir
-
-        expect:
-        registry.getDocumentationFor('gradle_daemon') == daemonPage.absolutePath
-    }
-
-    def "fails when local user guide is present in distribution but target page not found"() {
-        def distDir = tmpDir.createDir("home")
-        distDir.createFile("docs/userguide/userguide.html")
-        def expectedPage = distDir.file("docs/userguide/gradle_daemon.html")
-
-        given:
-        _ * locator.gradleHome >> distDir
-
-        when:
-        registry.getDocumentationFor('gradle_daemon')
-
-        then:
-        IllegalArgumentException e = thrown()
-        e.message == "User guide page '${expectedPage}' not found."
-    }
-
-    def "points users at the remote user guide when user guide not present in distribution"() {
-        def distDir = tmpDir.createDir("home")
-
-        given:
-        _ * locator.gradleHome >> distDir
-
-        expect:
-        registry.getDocumentationFor('gradle_daemon') == "http://gradle.org/docs/${gradleVersion.version}/userguide/gradle_daemon.html"
-    }
-
-    def "points users at the remote user guide when no distribution"() {
-        given:
-        _ * locator.gradleHome >> null
+    final DocumentationRegistry registry = new DocumentationRegistry()
 
+    def "points users at the gradle docs web site"() {
         expect:
         registry.getDocumentationFor('gradle_daemon') == "http://gradle.org/docs/${gradleVersion.version}/userguide/gradle_daemon.html"
     }
diff --git a/subprojects/core/src/test/groovy/org/gradle/api/internal/ExtensibleDynamicObjectTest.java b/subprojects/core/src/test/groovy/org/gradle/api/internal/ExtensibleDynamicObjectTest.java
index af3f768..85a7135 100644
--- a/subprojects/core/src/test/groovy/org/gradle/api/internal/ExtensibleDynamicObjectTest.java
+++ b/subprojects/core/src/test/groovy/org/gradle/api/internal/ExtensibleDynamicObjectTest.java
@@ -20,7 +20,7 @@ import groovy.lang.MissingMethodException;
 import org.gradle.api.internal.project.AbstractProject;
 import org.gradle.api.plugins.Convention;
 import org.gradle.testfixtures.ProjectBuilder;
-import org.gradle.util.HelperUtil;
+import org.gradle.util.TestUtil;
 import org.junit.Test;
 
 import java.util.Map;
@@ -413,7 +413,7 @@ public class ExtensibleDynamicObjectTest {
     @Test
     public void canInvokeMethodDefinedByScriptObject() {
         Bean bean = new Bean();
-        Script script = HelperUtil.createScript("def scriptMethod(a, b) { \"script:$a.$b\" } ");
+        Script script = TestUtil.createScript("def scriptMethod(a, b) { \"script:$a.$b\" } ");
         bean.extensibleDynamicObject.addObject(new BeanDynamicObject(script), ExtensibleDynamicObject.Location.BeforeConvention);
 
         assertTrue(bean.hasMethod("scriptMethod", "a", "b"));
@@ -484,7 +484,7 @@ public class ExtensibleDynamicObjectTest {
     @Test
     public void canInvokeClosurePropertyAsAMethod() {
         Bean bean = new Bean();
-        bean.setProperty("someMethod", HelperUtil.toClosure("{ param -> param.toLowerCase() }"));
+        bean.setProperty("someMethod", TestUtil.toClosure("{ param -> param.toLowerCase() }"));
         assertThat(bean.invokeMethod("someMethod", "Param"), equalTo((Object) "param"));
     }
 
diff --git a/subprojects/core/src/test/groovy/org/gradle/api/internal/GraphAggregatorTest.groovy b/subprojects/core/src/test/groovy/org/gradle/api/internal/GraphAggregatorTest.groovy
deleted file mode 100644
index b956b28..0000000
--- a/subprojects/core/src/test/groovy/org/gradle/api/internal/GraphAggregatorTest.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
-
-import spock.lang.Specification
-
-class GraphAggregatorTest extends Specification {
-    private final DirectedGraph<String, String> graph = Mock()
-    private final GraphAggregator aggregator = new GraphAggregator(graph)
-
-    def groupsNodeWithTheEntryNodeItIsReachableFrom() {
-        when:
-        def result = aggregator.group(['a'], ['a', 'b'])
-
-        then:
-        1 * graph.getNodeValues('a', !null, !null) >> { args -> args[2].add('b') }
-        result.getNodes('a') == ['a', 'b'] as Set
-    }
-
-    def groupsNodeWithTheClosesEntryNodeItIsReachableFrom() {
-        when:
-        def result = aggregator.group(['a', 'b'], ['a', 'b', 'c'])
-
-        then:
-        1 * graph.getNodeValues('a', !null, !null) >> { args -> args[2].add('b') }
-        1 * graph.getNodeValues('b', !null, !null) >> { args -> args[2].add('c') }
-        result.getNodes('a') == ['a'] as Set
-        result.getNodes('b') == ['b', 'c'] as Set
-    }
-
-    def groupsNodeWithMultipleEntryNodesWhenTheNodeHasMultipleClosesNodes() {
-        when:
-        def result = aggregator.group(['a', 'b'], ['a', 'b', 'c'])
-
-        then:
-        1 * graph.getNodeValues('a', !null, !null) >> { args -> args[2].add('c') }
-        1 * graph.getNodeValues('b', !null, !null) >> { args -> args[2].add('c') }
-        result.getNodes('a') == ['a', 'c'] as Set
-        result.getNodes('b') == ['b', 'c'] as Set
-    }
-
-    def groupsNodesWhichAreNotReachableFromStartNodes() {
-        when:
-        def result = aggregator.group(['a', 'b'], ['a', 'b', 'c', 'd'])
-
-        then:
-        1 * graph.getNodeValues('a', !null, !null) >> { args -> args[2].add('b') }
-        1 * graph.getNodeValues('c', !null, !null) >> { args -> args[2].add('d') }
-        result.topLevelNodes == ['a', 'b', 'c'] as Set
-        result.getNodes('c') == ['c', 'd'] as Set
-    }
-}
diff --git a/subprojects/core/src/test/groovy/org/gradle/api/internal/artifacts/DefaultExcludeRuleContainerTest.java b/subprojects/core/src/test/groovy/org/gradle/api/internal/artifacts/DefaultExcludeRuleContainerTest.java
index 48d2981..1ea7fd2 100644
--- a/subprojects/core/src/test/groovy/org/gradle/api/internal/artifacts/DefaultExcludeRuleContainerTest.java
+++ b/subprojects/core/src/test/groovy/org/gradle/api/internal/artifacts/DefaultExcludeRuleContainerTest.java
@@ -25,9 +25,6 @@ import java.util.*;
 import static org.hamcrest.Matchers.*;
 import static org.junit.Assert.assertThat;
 
-/**
- * @author Hans Dockter
- */
 public class DefaultExcludeRuleContainerTest {
     @Test
     public void testInit() {
diff --git a/subprojects/core/src/test/groovy/org/gradle/api/internal/artifacts/ProjectBackedModuleTest.groovy b/subprojects/core/src/test/groovy/org/gradle/api/internal/artifacts/ProjectBackedModuleTest.groovy
index 89a8e3e..dcca9cb 100644
--- a/subprojects/core/src/test/groovy/org/gradle/api/internal/artifacts/ProjectBackedModuleTest.groovy
+++ b/subprojects/core/src/test/groovy/org/gradle/api/internal/artifacts/ProjectBackedModuleTest.groovy
@@ -16,14 +16,14 @@
 
 package org.gradle.api.internal.artifacts
 
-import org.gradle.util.HelperUtil
+import org.gradle.util.TestUtil
 import spock.lang.Specification
 
 class ProjectBackedModuleTest extends Specification {
 
     def "module exposes project properties"() {
         given:
-        def project = HelperUtil.createRootProject()
+        def project = TestUtil.createRootProject()
         def module = new ProjectBackedModule(project)
 
         expect:
diff --git a/subprojects/core/src/test/groovy/org/gradle/api/internal/artifacts/dependencies/AbstractModuleDependencySpec.groovy b/subprojects/core/src/test/groovy/org/gradle/api/internal/artifacts/dependencies/AbstractModuleDependencySpec.groovy
index 653f467..e5a1bca 100644
--- a/subprojects/core/src/test/groovy/org/gradle/api/internal/artifacts/dependencies/AbstractModuleDependencySpec.groovy
+++ b/subprojects/core/src/test/groovy/org/gradle/api/internal/artifacts/dependencies/AbstractModuleDependencySpec.groovy
@@ -19,7 +19,7 @@ import org.gradle.api.artifacts.Dependency
 import org.gradle.api.artifacts.DependencyArtifact
 import org.gradle.api.artifacts.ModuleDependency
 import org.gradle.api.internal.artifacts.DefaultExcludeRule
-import org.gradle.util.HelperUtil
+import org.gradle.util.TestUtil
 import org.gradle.util.WrapUtil
 import spock.lang.Specification
 
@@ -83,7 +83,7 @@ class AbstractModuleDependencySpec extends Specification {
     }
 
     private DependencyArtifact createAnonymousArtifact() {
-        return new DefaultDependencyArtifact(HelperUtil.createUniqueId(), "type", "org", "classifier", "url")
+        return new DefaultDependencyArtifact(TestUtil.createUniqueId(), "type", "org", "classifier", "url")
     }
 
     public static void assertDeepCopy(ModuleDependency dependency, ModuleDependency copiedDependency) {
diff --git a/subprojects/core/src/test/groovy/org/gradle/api/internal/artifacts/dependencies/AbstractModuleDependencyTest.java b/subprojects/core/src/test/groovy/org/gradle/api/internal/artifacts/dependencies/AbstractModuleDependencyTest.java
index 58d2472..118ea7b 100644
--- a/subprojects/core/src/test/groovy/org/gradle/api/internal/artifacts/dependencies/AbstractModuleDependencyTest.java
+++ b/subprojects/core/src/test/groovy/org/gradle/api/internal/artifacts/dependencies/AbstractModuleDependencyTest.java
@@ -20,7 +20,7 @@ import org.gradle.api.artifacts.DependencyArtifact;
 import org.gradle.api.artifacts.ExcludeRule;
 import org.gradle.api.artifacts.ModuleDependency;
 import org.gradle.api.internal.artifacts.DefaultExcludeRule;
-import org.gradle.util.HelperUtil;
+import org.gradle.util.TestUtil;
 import org.gradle.util.JUnit4GroovyMockery;
 import org.gradle.util.WrapUtil;
 import org.jmock.integration.junit4.JMock;
@@ -35,9 +35,6 @@ import static org.hamcrest.Matchers.*;
 import static org.junit.Assert.assertThat;
 import static org.junit.Assert.assertTrue;
 
-/**
- * @author Hans Dockter
- */
 @RunWith(JMock.class)
 abstract public class AbstractModuleDependencyTest {
     //TODO SF rework the remaining coverage of this hierarchy in the spirit of AbstractModuleDependencySpec and DefaultProjectDependencyTest
@@ -85,7 +82,7 @@ abstract public class AbstractModuleDependencyTest {
     }
 
     private DependencyArtifact createAnonymousArtifact() {
-        return new DefaultDependencyArtifact(HelperUtil.createUniqueId(), "type", "org", "classifier", "url");
+        return new DefaultDependencyArtifact(TestUtil.createUniqueId(), "type", "org", "classifier", "url");
     }
 
     @Test
diff --git a/subprojects/core/src/test/groovy/org/gradle/api/internal/artifacts/dependencies/DefaultClientModuleTest.java b/subprojects/core/src/test/groovy/org/gradle/api/internal/artifacts/dependencies/DefaultClientModuleTest.java
index 6a3e98a..e99073c 100644
--- a/subprojects/core/src/test/groovy/org/gradle/api/internal/artifacts/dependencies/DefaultClientModuleTest.java
+++ b/subprojects/core/src/test/groovy/org/gradle/api/internal/artifacts/dependencies/DefaultClientModuleTest.java
@@ -23,9 +23,6 @@ import org.junit.Test;
 import static org.hamcrest.Matchers.*;
 import static org.junit.Assert.assertThat;
 
-/**
- * @author Hans Dockter
- */
 public class DefaultClientModuleTest extends AbstractModuleDependencyTest {
     private static final String TEST_GROUP = "org.gradle";
     private static final String TEST_NAME = "gradle-core";
diff --git a/subprojects/core/src/test/groovy/org/gradle/api/internal/artifacts/dependencies/DefaultDependencyArtifactTest.java b/subprojects/core/src/test/groovy/org/gradle/api/internal/artifacts/dependencies/DefaultDependencyArtifactTest.java
index e27779a..1194e84 100644
--- a/subprojects/core/src/test/groovy/org/gradle/api/internal/artifacts/dependencies/DefaultDependencyArtifactTest.java
+++ b/subprojects/core/src/test/groovy/org/gradle/api/internal/artifacts/dependencies/DefaultDependencyArtifactTest.java
@@ -16,12 +16,10 @@
 package org.gradle.api.internal.artifacts.dependencies;
 
 import org.gradle.api.artifacts.DependencyArtifact;
-import static org.junit.Assert.assertEquals;
 import org.junit.Test;
 
-/**
- * @author Hans Dockter
- */
+import static org.junit.Assert.assertEquals;
+
 public class DefaultDependencyArtifactTest {
     @Test
     public void testInit() {
diff --git a/subprojects/core/src/test/groovy/org/gradle/api/internal/artifacts/dependencies/DefaultExternalModuleDependencyTest.java b/subprojects/core/src/test/groovy/org/gradle/api/internal/artifacts/dependencies/DefaultExternalModuleDependencyTest.java
index d1b657c..1019446 100644
--- a/subprojects/core/src/test/groovy/org/gradle/api/internal/artifacts/dependencies/DefaultExternalModuleDependencyTest.java
+++ b/subprojects/core/src/test/groovy/org/gradle/api/internal/artifacts/dependencies/DefaultExternalModuleDependencyTest.java
@@ -27,9 +27,6 @@ import static org.hamcrest.Matchers.equalTo;
 import static org.junit.Assert.assertThat;
 
 
-/**
- * @author Hans Dockter
- */
 @RunWith(JMock.class)
 public class DefaultExternalModuleDependencyTest extends AbstractModuleDependencyTest {
     private static final String TEST_GROUP = "org.gradle";
diff --git a/subprojects/core/src/test/groovy/org/gradle/api/internal/artifacts/dependencies/DefaultProjectDependencyTest.groovy b/subprojects/core/src/test/groovy/org/gradle/api/internal/artifacts/dependencies/DefaultProjectDependencyTest.groovy
index 31bedb9..bc1b489 100644
--- a/subprojects/core/src/test/groovy/org/gradle/api/internal/artifacts/dependencies/DefaultProjectDependencyTest.groovy
+++ b/subprojects/core/src/test/groovy/org/gradle/api/internal/artifacts/dependencies/DefaultProjectDependencyTest.groovy
@@ -1,5 +1,5 @@
 /*
- * Copyright 2009 the original author or authors.
+ * Copyright 2013 the original author or authors.
  *
  * Licensed under the Apache License, Version 2.0 (the "License");
  * you may not use this file except in compliance with the License.
@@ -22,7 +22,7 @@ import org.gradle.api.internal.artifacts.DependencyResolveContext
 import org.gradle.api.internal.project.ProjectInternal
 import org.gradle.api.internal.tasks.TaskDependencyResolveContext
 import org.gradle.initialization.ProjectAccessListener
-import org.gradle.util.HelperUtil
+import org.gradle.util.TestUtil
 import spock.lang.Specification
 
 import static org.gradle.api.internal.artifacts.dependencies.AbstractModuleDependencySpec.assertDeepCopy
@@ -31,7 +31,7 @@ import static org.junit.Assert.assertThat
 
 class DefaultProjectDependencyTest extends Specification {
 
-    ProjectInternal project = HelperUtil.createRootProject()
+    ProjectInternal project = TestUtil.createRootProject()
     ProjectAccessListener listener = Mock()
 
     private projectDependency = new DefaultProjectDependency(project, null, false)
@@ -62,8 +62,8 @@ class DefaultProjectDependencyTest extends Specification {
     void "transitive resolution resolves all dependencies"() {
         def context = Mock(DependencyResolveContext)
 
-        def superConf = project.configurations.add("superConf")
-        def conf = project.configurations.add("conf")
+        def superConf = project.configurations.create("superConf")
+        def conf = project.configurations.create("conf")
         conf.extendsFrom(superConf)
 
         def dep1 = Mock(ProjectDependency)
@@ -110,7 +110,7 @@ class DefaultProjectDependencyTest extends Specification {
     void "is Buildable"() {
         def context = Mock(TaskDependencyResolveContext)
 
-        def conf = project.configurations.add('conf')
+        def conf = project.configurations.create('conf')
         def listener = Mock(ProjectAccessListener)
         projectDependency = new DefaultProjectDependency(project, 'conf', listener, true)
 
@@ -126,7 +126,7 @@ class DefaultProjectDependencyTest extends Specification {
 
     void "does not build project dependencies if configured so"() {
         def context = Mock(TaskDependencyResolveContext)
-        project.configurations.add('conf')
+        project.configurations.create('conf')
         projectDependency = new DefaultProjectDependency(project, 'conf', listener, false)
 
         when:
diff --git a/subprojects/core/src/test/groovy/org/gradle/api/internal/artifacts/dsl/dependencies/DefaultDependencyHandlerTest.groovy b/subprojects/core/src/test/groovy/org/gradle/api/internal/artifacts/dsl/dependencies/DefaultDependencyHandlerTest.groovy
index c58f082..534f9d4 100644
--- a/subprojects/core/src/test/groovy/org/gradle/api/internal/artifacts/dsl/dependencies/DefaultDependencyHandlerTest.groovy
+++ b/subprojects/core/src/test/groovy/org/gradle/api/internal/artifacts/dsl/dependencies/DefaultDependencyHandlerTest.groovy
@@ -15,12 +15,10 @@
  */
 package org.gradle.api.internal.artifacts.dsl.dependencies
 
-import spock.lang.Specification
 import org.gradle.api.artifacts.*
+import org.gradle.api.artifacts.dsl.ComponentMetadataHandler
+import spock.lang.Specification
 
-/**
- * @author Hans Dockter
- */
 class DefaultDependencyHandlerTest extends Specification {
     private static final String TEST_CONF_NAME = "someConf"
     private ConfigurationContainer configurationContainer = Mock()
@@ -29,7 +27,7 @@ class DefaultDependencyHandlerTest extends Specification {
     private ProjectFinder projectFinder = Mock()
     private DependencySet dependencySet = Mock()
 
-    private DefaultDependencyHandler dependencyHandler = new DefaultDependencyHandler(configurationContainer, dependencyFactory, projectFinder)
+    private DefaultDependencyHandler dependencyHandler = new DefaultDependencyHandler(configurationContainer, dependencyFactory, projectFinder, Stub(ComponentMetadataHandler))
 
     void setup() {
         _ * configurationContainer.findByName(TEST_CONF_NAME) >> configuration
diff --git a/subprojects/core/src/test/groovy/org/gradle/api/internal/artifacts/dsl/dependencies/ModuleFactoryDelegateTest.java b/subprojects/core/src/test/groovy/org/gradle/api/internal/artifacts/dsl/dependencies/ModuleFactoryDelegateTest.java
index 27fe2f6..e6b4772 100644
--- a/subprojects/core/src/test/groovy/org/gradle/api/internal/artifacts/dsl/dependencies/ModuleFactoryDelegateTest.java
+++ b/subprojects/core/src/test/groovy/org/gradle/api/internal/artifacts/dsl/dependencies/ModuleFactoryDelegateTest.java
@@ -20,7 +20,7 @@ import org.gradle.api.artifacts.ClientModule;
 import org.gradle.api.artifacts.Dependency;
 import org.gradle.api.artifacts.ModuleDependency;
 import org.gradle.api.internal.artifacts.dependencies.DefaultClientModule;
-import org.gradle.util.HelperUtil;
+import org.gradle.util.TestUtil;
 import org.gradle.util.WrapUtil;
 import org.hamcrest.Matchers;
 import org.jmock.Expectations;
@@ -29,9 +29,6 @@ import org.junit.Test;
 
 import static org.junit.Assert.assertThat;
 
-/**
- * @author Hans Dockter
- */
 public class ModuleFactoryDelegateTest {
     private JUnit4Mockery context = new JUnit4Mockery();
 
@@ -52,7 +49,7 @@ public class ModuleFactoryDelegateTest {
     @Test
     public void dependencyWithClosure() {
         final String dependencyNotation = "someNotation";
-        final Closure configureClosure = HelperUtil.toClosure("{}");
+        final Closure configureClosure = TestUtil.toClosure("{}");
         final ModuleDependency dependencyDummy = context.mock(ModuleDependency.class);
         letFactoryStubReturnDependency(dependencyNotation, dependencyDummy);
         moduleFactoryDelegate.dependency(dependencyNotation, configureClosure);
@@ -81,7 +78,7 @@ public class ModuleFactoryDelegateTest {
     @Test
     public void module() {
         final String clientModuleNotation = "someNotation";
-        final Closure configureClosure = HelperUtil.toClosure("{}");
+        final Closure configureClosure = TestUtil.toClosure("{}");
         final ClientModule clientModuleDummy = context.mock(ClientModule.class);
         context.checking(new Expectations() {{
             allowing(dependencyFactoryStub).createModule(clientModuleNotation, configureClosure);
diff --git a/subprojects/core/src/test/groovy/org/gradle/api/internal/artifacts/publish/AbstractPublishArtifactTest.java b/subprojects/core/src/test/groovy/org/gradle/api/internal/artifacts/publish/AbstractPublishArtifactTest.java
index b29f542..2a91119 100644
--- a/subprojects/core/src/test/groovy/org/gradle/api/internal/artifacts/publish/AbstractPublishArtifactTest.java
+++ b/subprojects/core/src/test/groovy/org/gradle/api/internal/artifacts/publish/AbstractPublishArtifactTest.java
@@ -21,14 +21,12 @@ import org.gradle.api.artifacts.PublishArtifact;
 import org.gradle.util.JUnit4GroovyMockery;
 import org.jmock.integration.junit4.JUnit4Mockery;
 import org.jmock.lib.legacy.ClassImposteriser;
-import static org.junit.Assert.assertEquals;
 
 import java.io.File;
 import java.util.Date;
 
-/**
- * @author Hans Dockter
- */
+import static org.junit.Assert.assertEquals;
+
 public abstract class AbstractPublishArtifactTest {
     private static final File TEST_FILE = new File("artifactFile");
     private static final String TEST_NAME = "myfile-1";
diff --git a/subprojects/core/src/test/groovy/org/gradle/api/internal/artifacts/publish/ArchivePublishArtifactTest.groovy b/subprojects/core/src/test/groovy/org/gradle/api/internal/artifacts/publish/ArchivePublishArtifactTest.groovy
index 4713781..b873b6d 100644
--- a/subprojects/core/src/test/groovy/org/gradle/api/internal/artifacts/publish/ArchivePublishArtifactTest.groovy
+++ b/subprojects/core/src/test/groovy/org/gradle/api/internal/artifacts/publish/ArchivePublishArtifactTest.groovy
@@ -26,9 +26,6 @@ import static org.hamcrest.Matchers.equalTo
 import static org.hamcrest.Matchers.sameInstance
 import static org.junit.Assert.assertThat
 
-/**
- * @author Hans Dockter
- */
 public class ArchivePublishArtifactTest extends AbstractPublishArtifactTest {
     private AbstractArchiveTask archiveTask = context.mock(AbstractArchiveTask.class)
 
diff --git a/subprojects/core/src/test/groovy/org/gradle/api/internal/artifacts/publish/DefaultPublishArtifactTest.java b/subprojects/core/src/test/groovy/org/gradle/api/internal/artifacts/publish/DefaultPublishArtifactTest.java
index e1a857f..681f65a 100644
--- a/subprojects/core/src/test/groovy/org/gradle/api/internal/artifacts/publish/DefaultPublishArtifactTest.java
+++ b/subprojects/core/src/test/groovy/org/gradle/api/internal/artifacts/publish/DefaultPublishArtifactTest.java
@@ -28,9 +28,6 @@ import static org.gradle.util.WrapUtil.toSet;
 import static org.hamcrest.Matchers.equalTo;
 import static org.junit.Assert.assertThat;
 
-/**
- * @author Hans Dockter
- */
 @RunWith(JMock.class)
 public class DefaultPublishArtifactTest extends AbstractPublishArtifactTest {
     protected PublishArtifact createPublishArtifact(String classifier) {
diff --git a/subprojects/core/src/test/groovy/org/gradle/api/internal/artifacts/repositories/ArtifactRepositoryChangingNameAfterContainerInclusionDeprecationTest.groovy b/subprojects/core/src/test/groovy/org/gradle/api/internal/artifacts/repositories/ArtifactRepositoryChangingNameAfterContainerInclusionDeprecationTest.groovy
index 428c62e..1e09674 100644
--- a/subprojects/core/src/test/groovy/org/gradle/api/internal/artifacts/repositories/ArtifactRepositoryChangingNameAfterContainerInclusionDeprecationTest.groovy
+++ b/subprojects/core/src/test/groovy/org/gradle/api/internal/artifacts/repositories/ArtifactRepositoryChangingNameAfterContainerInclusionDeprecationTest.groovy
@@ -21,26 +21,23 @@ import org.gradle.api.artifacts.repositories.ArtifactRepository
 import org.gradle.logging.ConfigureLogging
 import org.gradle.logging.TestAppender
 import org.gradle.util.DeprecationLogger
-import org.gradle.util.HelperUtil
+import org.gradle.util.TestUtil
+import org.junit.Rule
 import spock.lang.Specification
 import spock.lang.Unroll
 
 class ArtifactRepositoryChangingNameAfterContainerInclusionDeprecationTest extends Specification {
 
     TestAppender appender = new TestAppender()
-    ConfigureLogging logging
+    @Rule ConfigureLogging logging = new ConfigureLogging(appender)
     Project project
 
     def setup() {
-        project = HelperUtil.createRootProject()
+        project = TestUtil.createRootProject()
         DeprecationLogger.reset()
-        appender = new TestAppender()
-        logging = new ConfigureLogging(appender)
-        logging.attachAppender()
     }
 
     def cleanup() {
-        logging.detachAppender()
         DeprecationLogger.reset()
     }
 
diff --git a/subprojects/core/src/test/groovy/org/gradle/api/internal/artifacts/version/LatestVersionSemanticComparatorSpec.groovy b/subprojects/core/src/test/groovy/org/gradle/api/internal/artifacts/version/LatestVersionSemanticComparatorSpec.groovy
deleted file mode 100644
index b363c9d..0000000
--- a/subprojects/core/src/test/groovy/org/gradle/api/internal/artifacts/version/LatestVersionSemanticComparatorSpec.groovy
+++ /dev/null
@@ -1,76 +0,0 @@
-/*
- * Copyright 2012 the original author or authors.
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- *      http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY 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.version
-
-import spock.lang.Specification
-
-/**
- * by Szczepan Faber, created at: 10/9/12
- */
-class LatestVersionSemanticComparatorSpec extends Specification {
-
-    private comparator = new LatestVersionSemanticComparator()
-
-    def "compares versions"() {
-        expect:
-        comparator.compare(a, b) < 0
-        comparator.compare(b, a) > 0
-        comparator.compare(a, a) == 0
-        comparator.compare(b, b) == 0
-
-        where:
-        a                   | b
-        '1.0'               | '2.0'
-        '1.2'               | '1.10'
-        '1.0'               | '1.0.1'
-        '1.0-rc-1'          | '1.0-rc-2'
-        '1.0-alpha'         | '1.0'
-        '1.0-alpha'         | '1.0-beta'
-        '1.0-1'             | '1.0-2'
-        '1.0.a'             | '1.0.b'
-        '1.0.alpha'         | '1.0.b'
-    }
-
-    def "equal"() {
-        expect:
-        comparator.compare(a, b) == 0
-        comparator.compare(b, a) == 0
-
-        //some of the comparison are not working hence commented out.
-        //consider updating the implementation when we port the ivy comparison mechanism.
-        where:
-        a                   | b
-        '1.0'               | '1.0'
-        '5.0'               | '5.0'
-//        '1.0.0'             | '1.0'
-//        '1.0.0'             | '1'
-//        '1.0-alpha'         | '1.0-ALPHA'
-//        '1.0.alpha'         | '1.0-alpha'
-    }
-
-    def "not equal"() {
-        expect:
-        comparator.compare(a, b) != 0
-        comparator.compare(b, a) != 0
-
-        where:
-        a                   | b
-        '1.0'               | ''
-        '1.0'               | null
-        '1.0'               | 'hey joe'
-    }
-}
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
deleted file mode 100644
index 94a6498..0000000
--- a/subprojects/core/src/test/groovy/org/gradle/api/internal/changedetection/CacheBackedFileSnapshotRepositoryTest.groovy
+++ /dev/null
@@ -1,65 +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.api.internal.changedetection
-
-import org.gradle.cache.PersistentIndexedCache
-import spock.lang.Specification
-
-class CacheBackedFileSnapshotRepositoryTest extends Specification {
-    final TaskArtifactStateCacheAccess cacheAccess = Mock()
-    final PersistentIndexedCache<Object, Object> indexedCache = Mock()
-    FileSnapshotRepository repository
-
-    def setup() {
-        1 * cacheAccess.createCache("fileSnapshots", Object, Object) >> indexedCache
-        repository = new CacheBackedFileSnapshotRepository(cacheAccess)
-    }
-
-    def "assigns an id when a snapshot is added"() {
-        FileCollectionSnapshot snapshot = Mock()
-
-        when:
-        def id = repository.add(snapshot)
-
-        then:
-        id == 4
-        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
-        1 * indexedCache.get(4) >> snapshot
-        0 * _._
-    }
-
-    def "can delete a snapshot by id"() {
-        when:
-        repository.remove(4)
-
-        then:
-        1 * indexedCache.remove(4)
-        0 * _._
-    }
-}
diff --git a/subprojects/core/src/test/groovy/org/gradle/api/internal/changedetection/CachingHasherTest.java b/subprojects/core/src/test/groovy/org/gradle/api/internal/changedetection/CachingHasherTest.java
deleted file mode 100644
index 4082439..0000000
--- a/subprojects/core/src/test/groovy/org/gradle/api/internal/changedetection/CachingHasherTest.java
+++ /dev/null
@@ -1,108 +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.changedetection;
-
-import org.gradle.cache.PersistentIndexedCache;
-import org.gradle.messaging.serialize.Serializer;
-import org.gradle.test.fixtures.file.TestNameTestDirectoryProvider;
-import org.jmock.Expectations;
-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 static org.gradle.util.Matchers.reflectionEquals;
-import static org.hamcrest.Matchers.*;
-import static org.junit.Assert.assertThat;
-
- at RunWith(JMock.class)
-public class CachingHasherTest {
-    @Rule
-    TestNameTestDirectoryProvider tmpDir = new TestNameTestDirectoryProvider();
-    private final JUnit4Mockery context = new JUnit4Mockery();
-    private final Hasher delegate = context.mock(Hasher.class);
-    private final PersistentIndexedCache<File, CachingHasher.FileInfo> cache = context.mock(
-            PersistentIndexedCache.class);
-    private final TaskArtifactStateCacheAccess cacheAccess = context.mock(TaskArtifactStateCacheAccess.class);
-    private final byte[] hash = "hash".getBytes();
-    private final File file = tmpDir.createFile("testfile").write("content");
-    private CachingHasher hasher;
-
-    @Before
-    public void setup() {
-        context.checking(new Expectations(){{
-            one(cacheAccess).createCache(with(equalTo("fileHashes")), with(equalTo(File.class)), with(notNullValue(Class.class)), with(notNullValue(Serializer.class)));
-            will(returnValue(cache));
-        }});
-        hasher = new CachingHasher(delegate, cacheAccess);
-    }
-
-    @Test
-    public void hashesFileWhenHashNotCached() {
-        context.checking(new Expectations() {{
-            one(cache).get(file);
-            will(returnValue(null));
-            one(delegate).hash(file);
-            will(returnValue(hash));
-            one(cache).put(with(equalTo(file)), with(reflectionEquals(new CachingHasher.FileInfo(hash, file.length(),
-                    file.lastModified()))));
-        }});
-
-        assertThat(hasher.hash(file), sameInstance(hash));
-    }
-
-    @Test
-    public void hashesFileWhenLengthHasChanged() {
-        context.checking(new Expectations() {{
-            one(cache).get(file);
-            will(returnValue(new CachingHasher.FileInfo(hash, 1078, file.lastModified())));
-            one(delegate).hash(file);
-            will(returnValue(hash));
-            one(cache).put(with(equalTo(file)), with(reflectionEquals(new CachingHasher.FileInfo(hash, file.length(),
-                    file.lastModified()))));
-        }});
-
-        assertThat(hasher.hash(file), sameInstance(hash));
-    }
-
-    @Test
-    public void hashesFileWhenTimestampHasChanged() {
-        context.checking(new Expectations() {{
-            one(cache).get(file);
-            will(returnValue(new CachingHasher.FileInfo(hash, file.length(), 12)));
-            one(delegate).hash(file);
-            will(returnValue(hash));
-            one(cache).put(with(equalTo(file)), with(reflectionEquals(new CachingHasher.FileInfo(hash, file.length(),
-                    file.lastModified()))));
-        }});
-
-        assertThat(hasher.hash(file), sameInstance(hash));
-    }
-
-    @Test
-    public void doesNotHashFileWhenTimestampAndLengthHaveNotChanged() {
-        context.checking(new Expectations() {{
-            one(cache).get(file);
-            will(returnValue(new CachingHasher.FileInfo(hash, file.length(), file.lastModified())));
-        }});
-
-        assertThat(hasher.hash(file), sameInstance(hash));
-    }
-}
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
deleted file mode 100644
index ef36edf..0000000
--- a/subprojects/core/src/test/groovy/org/gradle/api/internal/changedetection/CompositeUpToDateRuleTest.groovy
+++ /dev/null
@@ -1,69 +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.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
deleted file mode 100755
index 961a356..0000000
--- a/subprojects/core/src/test/groovy/org/gradle/api/internal/changedetection/DefaultFileSnapshotterTest.groovy
+++ /dev/null
@@ -1,330 +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.changedetection
-
-import org.gradle.api.file.FileCollection
-import org.gradle.api.file.FileTree
-import org.gradle.test.fixtures.file.TestFile
-import org.gradle.test.fixtures.file.TestNameTestDirectoryProvider
-import org.gradle.util.ChangeListener
-import org.gradle.util.JUnit4GroovyMockery
-import org.jmock.integration.junit4.JMock
-import org.junit.Rule
-import org.junit.Test
-import org.junit.runner.RunWith
-
-import static org.hamcrest.Matchers.equalTo
-import static org.hamcrest.Matchers.notNullValue
-import static org.junit.Assert.assertThat
-
- 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 TestNameTestDirectoryProvider tmpDir = new TestNameTestDirectoryProvider()
-
-    @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/DefaultTaskArtifactStateCacheAccessTest.groovy b/subprojects/core/src/test/groovy/org/gradle/api/internal/changedetection/DefaultTaskArtifactStateCacheAccessTest.groovy
deleted file mode 100644
index afbbad1..0000000
--- a/subprojects/core/src/test/groovy/org/gradle/api/internal/changedetection/DefaultTaskArtifactStateCacheAccessTest.groovy
+++ /dev/null
@@ -1,53 +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.api.internal.changedetection
-
-import org.gradle.cache.CacheRepository
-import spock.lang.Specification
-import org.gradle.api.internal.GradleInternal
-import org.gradle.cache.DirectoryCacheBuilder
-import org.gradle.cache.PersistentCache
-import org.gradle.cache.PersistentIndexedCache
-
-class DefaultTaskArtifactStateCacheAccessTest extends Specification {
-    final GradleInternal gradle = Mock()
-    final CacheRepository cacheRepository = Mock()
-    final DefaultTaskArtifactStateCacheAccess cacheAccess = new DefaultTaskArtifactStateCacheAccess(gradle, cacheRepository)
-    
-    def "opens backing cache on first use"() {
-        DirectoryCacheBuilder cacheBuilder = Mock()
-        PersistentCache backingCache = Mock()
-        PersistentIndexedCache<String, Integer> backingIndexedCache = Mock()
-
-        when:
-        def indexedCache = cacheAccess.createCache("some-cache", String, Integer)
-
-        then:
-        0 * _._
-
-        when:
-        indexedCache.get("key")
-
-        then:
-        1 * cacheRepository.cache("taskArtifacts") >> cacheBuilder
-        1 * cacheBuilder.open() >> backingCache
-        _ * cacheBuilder._ >> cacheBuilder
-        _ * backingCache.baseDir >> new File("baseDir")
-        1 * backingCache.createCache(new File("baseDir/some-cache.bin"), String, Integer) >> backingIndexedCache
-        1 * backingIndexedCache.get("key")
-        0 * _._
-    }
-}
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
deleted file mode 100644
index c25ebbc..0000000
--- a/subprojects/core/src/test/groovy/org/gradle/api/internal/changedetection/DefaultTaskArtifactStateRepositoryTest.java
+++ /dev/null
@@ -1,606 +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.changedetection;
-
-import org.gradle.CacheUsage;
-import org.gradle.api.DefaultTask;
-import org.gradle.api.internal.TaskInternal;
-import org.gradle.api.internal.project.ProjectInternal;
-import org.gradle.api.invocation.Gradle;
-import org.gradle.cache.CacheRepository;
-import org.gradle.cache.internal.DefaultCacheRepository;
-import org.gradle.internal.id.RandomLongIdGenerator;
-import org.gradle.test.fixtures.file.TestFile;
-import org.gradle.test.fixtures.file.TestNameTestDirectoryProvider;
-import org.gradle.testfixtures.internal.InMemoryCacheFactory;
-import org.gradle.util.HelperUtil;
-import org.hamcrest.Matcher;
-import org.junit.Before;
-import org.junit.Rule;
-import org.junit.Test;
-
-import java.io.File;
-import java.util.*;
-
-import static org.gradle.util.Matchers.isEmpty;
-import static org.gradle.util.WrapUtil.*;
-import static org.hamcrest.Matchers.*;
-import static org.junit.Assert.*;
-
-public class DefaultTaskArtifactStateRepositoryTest {
-    @Rule
-    public TestNameTestDirectoryProvider tmpDir = new TestNameTestDirectoryProvider();
-    private final ProjectInternal project = HelperUtil.createRootProject();
-    private final Gradle gradle = project.getGradle();
-    private final TestFile outputFile = tmpDir.file("output-file");
-    private final TestFile outputDir = tmpDir.file("output-dir");
-    private final TestFile outputDirFile = outputDir.file("some-file");
-    private final TestFile outputDirFile2 = outputDir.file("some-file-2");
-    private final TestFile emptyOutputDir = tmpDir.file("empty-output-dir");
-    private final TestFile missingOutputFile = tmpDir.file("missing-output-file");
-    private final TestFile inputFile = tmpDir.createFile("input-file");
-    private final TestFile inputDir = tmpDir.createDir("input-dir");
-    private final TestFile inputDirFile = inputDir.file("input-file2").createFile();
-    private final TestFile missingInputFile = tmpDir.file("missing-input-file");
-    private final Set<TestFile> inputFiles = toSet(inputFile, inputDir, missingInputFile);
-    private final Set<TestFile> outputFiles = toSet(outputFile, outputDir, emptyOutputDir, missingOutputFile);
-    private final Set<TestFile> createFiles = toSet(outputFile, outputDirFile, outputDirFile2);
-    private DefaultTaskArtifactStateRepository repository;
-
-    @Before
-    public void setup() {
-        CacheRepository cacheRepository = new DefaultCacheRepository(tmpDir.createDir("user-home"), null, CacheUsage.ON, new InMemoryCacheFactory());
-        TaskArtifactStateCacheAccess cacheAccess = new DefaultTaskArtifactStateCacheAccess(gradle, cacheRepository);
-        FileSnapshotter inputFilesSnapshotter = new DefaultFileSnapshotter(new DefaultHasher());
-        FileSnapshotter outputFilesSnapshotter = new OutputFilesSnapshotter(inputFilesSnapshotter, new RandomLongIdGenerator(), cacheAccess);
-        TaskHistoryRepository taskHistoryRepository = new CacheBackedTaskHistoryRepository(cacheAccess, new CacheBackedFileSnapshotRepository(cacheAccess));
-        repository = new DefaultTaskArtifactStateRepository(taskHistoryRepository, inputFilesSnapshotter, outputFilesSnapshotter);
-    }
-
-    @Test
-    public void artifactsAreNotUpToDateWhenCacheIsEmpty() {
-        TaskArtifactState state = repository.getStateFor(task());
-        assertNotNull(state);
-        assertFalse(state.isUpToDate());
-    }
-
-    @Test
-    public void artifactsAreNotUpToDateWhenAnyOutputFileNoLongerExists() {
-        execute();
-
-        outputFile.delete();
-
-        TaskArtifactState state = repository.getStateFor(task());
-        assertFalse(state.isUpToDate());
-    }
-
-    @Test
-    public void artifactsAreNotUpToDateWhenAnyFileInOutputDirNoLongerExists() {
-        execute();
-
-        outputDirFile.delete();
-
-        TaskArtifactState state = repository.getStateFor(task());
-        assertFalse(state.isUpToDate());
-    }
-
-    @Test
-    public void artifactsAreNotUpToDateWhenAnyOutputFileHasChangedType() {
-        execute();
-
-        outputFile.delete();
-        outputFile.createDir();
-
-        TaskArtifactState state = repository.getStateFor(task());
-        assertFalse(state.isUpToDate());
-    }
-
-    @Test
-    public void artifactsAreNotUpToDateWhenAnyFileInOutputDirHasChangedType() {
-        execute();
-
-        outputDirFile.delete();
-        outputDirFile.createDir();
-
-        TaskArtifactState state = repository.getStateFor(task());
-        assertFalse(state.isUpToDate());
-    }
-
-    @Test
-    public void artifactsAreNotUpToDateWhenAnyOutputFileHasChangedHash() {
-        execute();
-
-        outputFile.write("new content");
-
-        TaskArtifactState state = repository.getStateFor(task());
-        assertFalse(state.isUpToDate());
-    }
-
-    @Test
-    public void artifactsAreNotUpToDateWhenAnyFileInOutputDirHasChangedHash() {
-        execute();
-
-        outputDirFile.write("new content");
-
-        TaskArtifactState state = repository.getStateFor(task());
-        assertFalse(state.isUpToDate());
-    }
-
-    @Test
-    public void artifactsAreNotUpToDateWhenAnyOutputFilesAddedToSet() {
-        execute();
-
-        TaskInternal task = builder().withOutputFiles(outputFile, outputDir, tmpDir.createFile("output-file-2"), emptyOutputDir, missingOutputFile).task();
-
-        TaskArtifactState state = repository.getStateFor(task);
-        assertFalse(state.isUpToDate());
-    }
-
-    @Test
-    public void artifactsAreNotUpToDateWhenAnyOutputFilesRemovedFromSet() {
-        execute();
-
-        TaskInternal task = builder().withOutputFiles(outputFile, emptyOutputDir, missingOutputFile).task();
-
-        TaskArtifactState state = repository.getStateFor(task);
-        assertFalse(state.isUpToDate());
-    }
-
-    @Test
-    public void artifactsAreNotUpToDateWhenTaskWithDifferentTypeGeneratedAnyOutputFiles() {
-        TaskInternal task1 = builder().withOutputFiles(outputFile).task();
-        TaskInternal task2 = builder().withType(TaskSubType.class).withOutputFiles(outputFile).task();
-
-        execute(task1, task2);
-
-        TaskArtifactState state = repository.getStateFor(task1);
-        assertFalse(state.isUpToDate());
-    }
-
-    @Test
-    public void artifactsAreNotUpToDateWhenAnyInputFilesAddedToSet() {
-        execute();
-
-        TaskInternal task = builder().withInputFiles(inputFile, inputDir, tmpDir.createFile("other-input"), missingInputFile).task();
-        TaskArtifactState state = repository.getStateFor(task);
-        assertFalse(state.isUpToDate());
-    }
-
-    @Test
-    public void artifactsAreNotUpToDateWhenAnyInputFilesRemovedFromSet() {
-        execute();
-
-        TaskInternal task = builder().withInputFiles(inputFile).task();
-        TaskArtifactState state = repository.getStateFor(task);
-        assertFalse(state.isUpToDate());
-    }
-
-    @Test
-    public void artifactsAreNotUpToDateWhenAnyInputFileHasChangedHash() {
-        execute();
-
-        inputFile.write("some new content");
-
-        TaskArtifactState state = repository.getStateFor(task());
-        assertFalse(state.isUpToDate());
-    }
-
-    @Test
-    public void artifactsAreNotUpToDateWhenAnyInputFileHasChangedType() {
-        execute();
-
-        inputFile.delete();
-        inputFile.createDir();
-
-        TaskArtifactState state = repository.getStateFor(task());
-        assertFalse(state.isUpToDate());
-    }
-
-    @Test
-    public void artifactsAreNotUpToDateWhenAnyInputFileNoLongerExists() {
-        execute();
-
-        inputFile.delete();
-
-        TaskArtifactState state = repository.getStateFor(task());
-        assertFalse(state.isUpToDate());
-    }
-
-    @Test
-    public void artifactsAreNotUpToDateWhenAnyFileCreatedInInputDir() {
-        execute();
-
-        inputDir.file("other-file").createFile();
-
-        TaskArtifactState state = repository.getStateFor(task());
-        assertFalse(state.isUpToDate());
-    }
-
-    @Test
-    public void artifactsAreNotUpToDateWhenAnyFileDeletedFromInputDir() {
-        execute();
-
-        inputDirFile.delete();
-
-        TaskArtifactState state = repository.getStateFor(task());
-        assertFalse(state.isUpToDate());
-    }
-
-    @Test
-    public void artifactsAreNotUpToDateWhenAnyFileInInputDirChangesHash() {
-        execute();
-
-        inputDirFile.writelns("new content");
-
-        TaskArtifactState state = repository.getStateFor(task());
-        assertFalse(state.isUpToDate());
-    }
-
-    @Test
-    public void artifactsAreNotUpToDateWhenAnyFileInInputDirChangesType() {
-        execute();
-
-        inputDirFile.delete();
-        inputDirFile.mkdir();
-
-        TaskArtifactState state = repository.getStateFor(task());
-        assertFalse(state.isUpToDate());
-    }
-
-    @Test
-    public void artifactsAreNotUpToDateWhenAnyInputPropertyValueChanged() {
-        execute();
-
-        TaskArtifactState state = repository.getStateFor(builder().withProperty("prop", "new value").task());
-        assertFalse(state.isUpToDate());
-    }
-
-    @Test
-    public void inputPropertyValueCanBeNull() {
-        TaskInternal task = builder().withProperty("prop", null).task();
-        execute(task);
-
-        TaskArtifactState state = repository.getStateFor(task);
-        assertTrue(state.isUpToDate());
-    }
-
-    @Test
-    public void artifactsAreNotUpToDateWhenAnyInputPropertyAdded() {
-        execute();
-
-        TaskArtifactState state = repository.getStateFor(builder().withProperty("prop2", "value").task());
-        assertFalse(state.isUpToDate());
-    }
-
-    @Test
-    public void artifactsAreNotUpToDateWhenAnyInputPropertyRemoved() {
-        execute(builder().withProperty("prop2", "value").task());
-
-        TaskArtifactState state = repository.getStateFor(task());
-        assertFalse(state.isUpToDate());
-    }
-
-    @Test
-    public void artifactsAreNotUpToDateWhenStateHasNotBeenUpdated() {
-        repository.getStateFor(task());
-
-        TaskArtifactState state = repository.getStateFor(task());
-        assertFalse(state.isUpToDate());
-    }
-
-    @Test
-    public void artifactsAreNotUpToDateWhenOutputDirWhichUsedToExistHasBeenDeleted() {
-        // Output dir already exists before first execution of task
-        outputDirFile.createFile();
-
-        TaskInternal task1 = builder().withOutputFiles(outputDir).createsFiles(outputDirFile).task();
-        TaskInternal task2 = builder().withPath("other").withOutputFiles(outputDir).createsFiles(outputDirFile2).task();
-
-        TaskArtifactState state = repository.getStateFor(task1);
-        assertFalse(state.isUpToDate());
-        state.afterTask();
-
-        outputDir.deleteDir();
-
-        // Another task creates dir
-        state = repository.getStateFor(task2);
-        assertFalse(state.isUpToDate());
-        task2.execute();
-        state.afterTask();
-
-        // Task should be out-of-date
-        state = repository.getStateFor(task1);
-        assertFalse(state.isUpToDate());
-    }
-
-    @Test
-    public void artifactsAreUpToDateWhenNothingHasChangedSinceOutputFilesWereGenerated() {
-        execute();
-
-        TaskArtifactState state = repository.getStateFor(task());
-        assertTrue(state.isUpToDate());
-
-        state = repository.getStateFor(task());
-        assertTrue(state.isUpToDate());
-    }
-
-    @Test
-    public void artifactsAreUpToDateWhenOutputFileWhichDidNotExistNowExists() {
-        execute();
-
-        missingOutputFile.touch();
-
-        TaskArtifactState state = repository.getStateFor(task());
-        assertTrue(state.isUpToDate());
-    }
-
-    @Test
-    public void artifactsAreUpToDateWhenOutputDirWhichWasEmptyIsNoLongerEmpty() {
-        execute();
-
-        emptyOutputDir.file("some-file").touch();
-
-        TaskArtifactState state = repository.getStateFor(task());
-        assertTrue(state.isUpToDate());
-    }
-
-    @Test
-    public void hasEmptyTaskHistoryWhenTaskHasNeverBeenExecuted() {
-        TaskArtifactState state = repository.getStateFor(task());
-        assertThat(state.getExecutionHistory().getOutputFiles().getFiles(), isEmpty());
-    }
-
-    @Test
-    public void hasTaskHistoryFromPreviousExecution() {
-        execute();
-
-        TaskArtifactState state = repository.getStateFor(task());
-        assertThat(state.getExecutionHistory().getOutputFiles().getFiles(), equalTo(toLinkedSet((File) outputFile, outputDirFile, outputDirFile2)));
-    }
-
-    @Test
-    public void multipleTasksCanProduceFilesIntoTheSameOutputDirectory() {
-        TaskInternal task1 = task();
-        TaskInternal task2 = builder().withPath("other").withOutputFiles(outputDir).createsFiles(outputDir.file("output2")).task();
-        execute(task1, task2);
-
-        TaskArtifactState state = repository.getStateFor(task1);
-        assertTrue(state.isUpToDate());
-
-        state = repository.getStateFor(task2);
-        assertTrue(state.isUpToDate());
-    }
-
-    @Test
-    public void multipleTasksCanProduceTheSameFileWithTheSameContents() {
-        TaskInternal task1 = builder().withOutputFiles(outputFile).task();
-        TaskInternal task2 = builder().withPath("other").withOutputFiles(outputFile).task();
-        execute(task1, task2);
-
-        TaskArtifactState state = repository.getStateFor(task1);
-        assertTrue(state.isUpToDate());
-
-        state = repository.getStateFor(task2);
-        assertTrue(state.isUpToDate());
-    }
-
-    @Test
-    public void multipleTasksCanProduceTheSameEmptyDir() {
-        TaskInternal task1 = task();
-        TaskInternal task2 = builder().withPath("other").withOutputFiles(outputDir).task();
-        execute(task1, task2);
-
-        TaskArtifactState state = repository.getStateFor(task1);
-        assertTrue(state.isUpToDate());
-
-        state = repository.getStateFor(task2);
-        assertTrue(state.isUpToDate());
-    }
-
-    @Test
-    public void doesNotConsiderExistingFilesInOutputDirectoryAsProducedByTask() {
-        TestFile otherFile = outputDir.file("other").createFile();
-
-        execute();
-
-        otherFile.delete();
-
-        TaskArtifactState state = repository.getStateFor(task());
-        assertTrue(state.isUpToDate());
-        assertThat(state.getExecutionHistory().getOutputFiles().getFiles(), (Matcher) not(hasItem(otherFile)));
-    }
-
-    @Test
-    public void considersExistingFileInOutputDirectoryWhichIsUpdatedByTheTaskAsProducedByTask() {
-        TestFile otherFile = outputDir.file("other").createFile();
-
-        TaskInternal task = task();
-        TaskArtifactState state = repository.getStateFor(task);
-        assertFalse(state.isUpToDate());
-
-        task.execute();
-        otherFile.write("new content");
-
-        state.afterTask();
-
-        otherFile.delete();
-
-        state = repository.getStateFor(task());
-        assertFalse(state.isUpToDate());
-        assertThat(state.getExecutionHistory().getOutputFiles().getFiles(), hasItem((File) otherFile));
-    }
-
-    @Test
-    public void fileIsNoLongerConsideredProducedByTaskOnceItIsDeleted() {
-        execute();
-
-        outputDirFile.delete();
-
-        TaskArtifactState state = repository.getStateFor(task());
-        assertFalse(state.isUpToDate());
-        state.afterTask();
-
-        outputDirFile.write("ignore me");
-
-        state = repository.getStateFor(task());
-        assertTrue(state.isUpToDate());
-        assertThat(state.getExecutionHistory().getOutputFiles().getFiles(), not(hasItem((File) outputDirFile)));
-        state.afterTask();
-    }
-
-    @Test
-    public void artifactsAreUpToDateWhenTaskDoesNotAcceptAnyInputs() {
-        TaskInternal task = builder().doesNotAcceptInput().task();
-        execute(task);
-
-        TaskArtifactState state = repository.getStateFor(task);
-        assertTrue(state.isUpToDate());
-
-        outputDirFile.delete();
-
-        state = repository.getStateFor(task);
-        assertFalse(state.isUpToDate());
-    }
-
-    @Test
-    public void artifactsAreUpToDateWhenTaskHasNoInputFiles() {
-        TaskInternal task = builder().withInputFiles().task();
-        execute(task);
-
-        TaskArtifactState state = repository.getStateFor(task);
-        assertTrue(state.isUpToDate());
-    }
-
-    @Test
-    public void artifactsAreUpToDateWhenTaskHasNoOutputs() {
-        TaskInternal task = builder().withOutputFiles().task();
-        execute(task);
-
-        TaskArtifactState state = repository.getStateFor(task);
-        assertTrue(state.isUpToDate());
-    }
-
-    @Test
-    public void taskCanProduceIntoDifferentSetsOfOutputFiles() {
-        TestFile outputDir2 = tmpDir.createDir("output-dir-2");
-        TestFile outputDirFile2 = outputDir2.file("output-file-2");
-        TaskInternal instance1 = builder().withOutputFiles(outputDir).createsFiles(outputDirFile).task();
-        TaskInternal instance2 = builder().withOutputFiles(outputDir2).createsFiles(outputDirFile2).task();
-
-        execute(instance1, instance2);
-
-        TaskArtifactState state = repository.getStateFor(instance1);
-        assertTrue(state.isUpToDate());
-        assertThat(state.getExecutionHistory().getOutputFiles().getFiles(), equalTo(toLinkedSet((File) outputDirFile)));
-
-        state = repository.getStateFor(instance2);
-        assertTrue(state.isUpToDate());
-        assertThat(state.getExecutionHistory().getOutputFiles().getFiles(), equalTo(toLinkedSet((File) outputDirFile2)));
-    }
-
-    private void execute() {
-        execute(task());
-    }
-
-    private void execute(TaskInternal... tasks) {
-        for (TaskInternal task : tasks) {
-            TaskArtifactState state = repository.getStateFor(task);
-            state.isUpToDate();
-            task.execute();
-            state.afterTask();
-        }
-    }
-
-    private TaskInternal task() {
-        return builder().task();
-    }
-
-    private TaskBuilder builder() {
-        return new TaskBuilder();
-    }
-
-    private class TaskBuilder {
-        private String path = "task";
-        private Collection<? extends File> inputs = inputFiles;
-        private Collection<? extends File> outputs = outputFiles;
-        private Collection<? extends TestFile> create = createFiles;
-        private Class<? extends TaskInternal> type = TaskInternal.class;
-        private Map<String, Object> inputProperties = new HashMap<String, Object>(toMap("prop", "value"));
-
-        TaskBuilder withInputFiles(File... inputFiles) {
-            inputs = Arrays.asList(inputFiles);
-            return this;
-        }
-
-        TaskBuilder withOutputFiles(File... outputFiles) {
-            outputs = Arrays.asList(outputFiles);
-            return this;
-        }
-
-        TaskBuilder createsFiles(TestFile... outputFiles) {
-            create = Arrays.asList(outputFiles);
-            return this;
-        }
-
-        TaskBuilder withPath(String path) {
-            this.path = path;
-            return this;
-        }
-
-        TaskBuilder withType(Class<? extends TaskInternal> type) {
-            this.type = type;
-            return this;
-        }
-
-        TaskBuilder doesNotAcceptInput() {
-            inputs = null;
-            inputProperties = null;
-            return this;
-        }
-
-        public TaskBuilder withProperty(String name, Object value) {
-            inputProperties.put(name, value);
-            return this;
-        }
-
-        TaskInternal task() {
-            final TaskInternal task = HelperUtil.createTask(type, project, path);
-            if (inputs != null) {
-                task.getInputs().files(inputs);
-            }
-            if (inputProperties != null) {
-                task.getInputs().properties(inputProperties);
-            }
-            if (outputs != null) {
-                task.getOutputs().files(outputs);
-            }
-            task.doLast(new org.gradle.api.Action<Object>() {
-                public void execute(Object o) {
-                    for (TestFile file : create) {
-                        file.createFile();
-                    }
-                }
-            });
-
-            return task;
-        }
-    }
-
-    public static class TaskSubType extends DefaultTask {
-    }
-
-}
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
deleted file mode 100644
index b7f1794..0000000
--- a/subprojects/core/src/test/groovy/org/gradle/api/internal/changedetection/FileCacheBroadcastTaskArtifactStateRepositoryTest.groovy
+++ /dev/null
@@ -1,107 +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.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
deleted file mode 100755
index ecec7f5..0000000
--- a/subprojects/core/src/test/groovy/org/gradle/api/internal/changedetection/ShortCircuitTaskArtifactStateRepositoryTest.java
+++ /dev/null
@@ -1,165 +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.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.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 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);
-
-    @Test
-    public void doesNotCreateStateObjectWhenTaskHasNotDeclaredAnyOutputs() {
-        TaskInternal task = taskWithNoOutputs();
-        TaskArtifactState state = repository.getStateFor(task);
-        assertNotNull(state);
-
-        assertFalse(state.isUpToDate());
-        state.beforeTask();
-        state.afterTask();
-        state.finished();
-    }
-    
-    @Test
-    public void delegatesToBackingRepositoryToCreateStateObjectForTaskThatHasDeclaredSomeOutputs() {
-        TaskInternal task = taskWithOutputs();
-        expectTaskStateCreated(task);
-
-        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 taskArtifactsAreOutOfDateWhenStartParameterOverrideNoOptIsSet() {
-        TaskInternal task = taskWithOutputs();
-        expectTaskStateCreated(task);
-
-        TaskArtifactState state = repository.getStateFor(task);
-
-        startParameter.setRerunTasks(true);
-        assertFalse(state.isUpToDate());
-    }
-
-    @Test
-    public void taskArtifactsAreOutOfDateWhenStartParameterOverrideRerunTasksIsSet() {
-        TaskInternal task = taskWithOutputs();
-        expectTaskStateCreated(task);
-
-        TaskArtifactState state = repository.getStateFor(task);
-
-        startParameter.setRerunTasks(true);
-        assertFalse(state.isUpToDate());
-    }
-
-    @Test
-    public void taskArtifactsAreOutOfDateWhenUpToDateSpecIsFalse() {
-        final TaskInternal task = taskWithOutputs();
-        expectTaskStateCreated(task);
-
-        TaskArtifactState state = repository.getStateFor(task);
-
-        context.checking(new Expectations() {{
-            one(upToDateSpec).isSatisfiedBy(task);
-            will(returnValue(false));
-        }});
-
-        assertFalse(state.isUpToDate());
-    }
-
-    @Test
-    public void determinesWhetherTaskArtifactsAreUpToDateUsingBackingRepository() {
-        final TaskInternal task = taskWithOutputs();
-        expectTaskStateCreated(task);
-
-        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(final TaskInternal task) {
-        context.checking(new Expectations() {{
-            one(delegate).getStateFor(task);
-            will(returnValue(taskArtifactState));
-        }});
-    }
-
-    private TaskInternal taskWithOutputs() {
-        final TaskInternal task = context.mock(TaskInternal.class);
-        context.checking(new Expectations() {{
-            allowing(task).getOutputs();
-            will(returnValue(taskOutputsInternal));
-            allowing(taskOutputsInternal).getHasOutput();
-            will(returnValue(true));
-            allowing(taskOutputsInternal).getUpToDateSpec();
-            will(returnValue(upToDateSpec));
-        }});
-
-        return task;
-    }
-
-    private TaskInternal taskWithNoOutputs() {
-        final TaskInternal task = context.mock(TaskInternal.class);
-        context.checking(new Expectations() {{
-            allowing(task).getOutputs();
-            will(returnValue(taskOutputsInternal));
-            allowing(taskOutputsInternal).getHasOutput();
-            will(returnValue(false));
-        }});
-
-        return task;
-    }
-}
diff --git a/subprojects/core/src/test/groovy/org/gradle/api/internal/changedetection/changes/DefaultTaskArtifactStateRepositoryTest.groovy b/subprojects/core/src/test/groovy/org/gradle/api/internal/changedetection/changes/DefaultTaskArtifactStateRepositoryTest.groovy
new file mode 100644
index 0000000..4b20daa
--- /dev/null
+++ b/subprojects/core/src/test/groovy/org/gradle/api/internal/changedetection/changes/DefaultTaskArtifactStateRepositoryTest.groovy
@@ -0,0 +1,683 @@
+/*
+ * Copyright 2013 the original author or authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY 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.changes
+import org.gradle.CacheUsage
+import org.gradle.api.Action
+import org.gradle.api.DefaultTask
+import org.gradle.api.internal.TaskInternal
+import org.gradle.api.internal.changedetection.TaskArtifactState
+import org.gradle.api.internal.changedetection.state.*
+import org.gradle.api.tasks.incremental.InputFileDetails
+import org.gradle.cache.CacheRepository
+import org.gradle.cache.internal.DefaultCacheRepository
+import org.gradle.internal.id.RandomLongIdGenerator
+import org.gradle.internal.reflect.DirectInstantiator
+import org.gradle.test.fixtures.file.TestFile
+import org.gradle.test.fixtures.file.TestNameTestDirectoryProvider
+import org.gradle.testfixtures.internal.InMemoryCacheFactory
+import org.gradle.util.TestUtil
+import org.junit.Rule
+import spock.lang.Specification
+
+import static org.gradle.util.WrapUtil.toMap
+import static org.gradle.util.WrapUtil.toSet
+
+public class DefaultTaskArtifactStateRepositoryTest extends Specification {
+    
+    @Rule
+    public TestNameTestDirectoryProvider tmpDir = new TestNameTestDirectoryProvider()
+    final project = TestUtil.createRootProject()
+    final gradle = project.getGradle()
+    final outputFile = tmpDir.file("output-file")
+    final outputDir = tmpDir.file("output-dir")
+    final outputDirFile = outputDir.file("some-file")
+    final outputDirFile2 = outputDir.file("some-file-2")
+    final emptyOutputDir = tmpDir.file("empty-output-dir")
+    final missingOutputFile = tmpDir.file("missing-output-file")
+    final inputFile = tmpDir.createFile("input-file")
+    final inputDir = tmpDir.createDir("input-dir")
+    final inputDirFile = inputDir.file("input-file2").createFile()
+    final missingInputFile = tmpDir.file("missing-input-file")
+    final inputFiles = toSet(inputFile, inputDir, missingInputFile)
+    final outputFiles = toSet(outputFile, outputDir, emptyOutputDir, missingOutputFile)
+    final createFiles = toSet(outputFile, outputDirFile, outputDirFile2)
+    TaskInternal task = builder.task()
+    DefaultTaskArtifactStateRepository repository
+
+
+    def setup() {
+        CacheRepository cacheRepository = new DefaultCacheRepository(tmpDir.createDir("user-home"), null, CacheUsage.ON, new InMemoryCacheFactory())
+        TaskArtifactStateCacheAccess cacheAccess = new DefaultTaskArtifactStateCacheAccess(gradle, cacheRepository)
+        FileSnapshotter inputFilesSnapshotter = new DefaultFileSnapshotter(new DefaultHasher(), cacheAccess)
+        FileSnapshotter outputFilesSnapshotter = new OutputFilesSnapshotter(inputFilesSnapshotter, new RandomLongIdGenerator(), cacheAccess)
+        TaskHistoryRepository taskHistoryRepository = new CacheBackedTaskHistoryRepository(cacheAccess, new CacheBackedFileSnapshotRepository(cacheAccess, new RandomLongIdGenerator()))
+        repository = new DefaultTaskArtifactStateRepository(taskHistoryRepository, new DirectInstantiator(), outputFilesSnapshotter, inputFilesSnapshotter)
+    }
+
+    def artifactsAreNotUpToDateWhenCacheIsEmpty() {
+        expect:
+        outOfDate(task)
+    }
+
+    def artifactsAreNotUpToDateWhenAnyOutputFileNoLongerExists() {
+        given:
+        execute(task)
+
+        when:
+        outputFile.delete()
+
+        then:
+        outOfDate task
+    }
+
+    def artifactsAreNotUpToDateWhenAnyFileInOutputDirNoLongerExists() {
+        given:
+        execute(task)
+
+        when:
+        outputDirFile.delete()
+
+        then:
+        outOfDate task
+    }
+
+    def artifactsAreNotUpToDateWhenAnyOutputFileHasChangedType() {
+        given:
+        execute(task)
+
+        when:
+        outputFile.delete()
+        outputFile.createDir()
+
+        then:
+        outOfDate task
+    }
+
+    def artifactsAreNotUpToDateWhenAnyFileInOutputDirHasChangedType() {
+        given:
+        execute(task)
+
+        when:
+        outputDirFile.delete()
+        outputDirFile.createDir()
+
+        then:
+        outOfDate task
+    }
+
+    def artifactsAreNotUpToDateWhenAnyOutputFileHasChangedHash() {
+        given:
+        execute(task)
+        
+        when:
+        outputFile.write("new content")
+
+        then:
+        outOfDate task
+    }
+
+    def artifactsAreNotUpToDateWhenAnyFileInOutputDirHasChangedHash() {
+        given:
+        execute(task)
+        
+        when:
+        outputDirFile.write("new content")
+
+        then:
+        outOfDate task
+    }
+
+    def artifactsAreNotUpToDateWhenAnyOutputFilesAddedToSet() {
+        when:
+        execute(task)
+        TaskInternal outputFilesAddedTask = builder.withOutputFiles(outputFile, outputDir, tmpDir.createFile("output-file-2"), emptyOutputDir, missingOutputFile).task()
+
+        then:
+        outOfDate outputFilesAddedTask
+    }
+
+    def artifactsAreNotUpToDateWhenAnyOutputFilesRemovedFromSet() {
+        when:
+        execute(task)
+        TaskInternal outputFilesRemovedTask = builder.withOutputFiles(outputFile, emptyOutputDir, missingOutputFile).task()
+
+        then:
+        outOfDate outputFilesRemovedTask
+    }
+
+    def artifactsAreNotUpToDateWhenTaskWithDifferentTypeGeneratedAnyOutputFiles() {
+        when:
+        TaskInternal task1 = builder.withOutputFiles(outputFile).task()
+        TaskInternal task2 = builder.withType(TaskSubType.class).withOutputFiles(outputFile).task()
+        execute(task1, task2)
+
+        then:
+        outOfDate task
+    }
+
+    def artifactsAreNotUpToDateWhenAnyInputFilesAddedToSet() {
+        final addedFile = tmpDir.createFile("other-input")
+
+        when:
+        execute(task)
+        TaskInternal inputFilesAdded = builder.withInputFiles(inputFile, inputDir, addedFile, missingInputFile).task()
+
+        then:
+        inputsOutOfDate(inputFilesAdded).withAddedFile(addedFile)
+    }
+
+    def artifactsAreNotUpToDateWhenAnyInputFilesRemovedFromSet() {
+        when:
+        execute(task)
+        TaskInternal inputFilesRemoved = builder.withInputFiles(inputFile).task()
+
+        then:
+        inputsOutOfDate(inputFilesRemoved).withRemovedFile(inputDirFile)
+    }
+
+    def artifactsAreNotUpToDateWhenAnyInputFileHasChangedHash() {
+        given:
+        execute(task)
+
+        when:
+        inputFile.write("some new content")
+
+        then:
+        inputsOutOfDate(task).withModifiedFile(inputFile)
+    }
+
+    def artifactsAreNotUpToDateWhenAnyInputFileHasChangedType() {
+        given:
+        execute(task)
+
+        when:
+        inputFile.delete()
+        inputFile.createDir()
+
+        then:
+        inputsOutOfDate(task).withRemovedFile(inputFile)
+    }
+
+    def artifactsAreNotUpToDateWhenAnyInputFileNoLongerExists() {
+        given:
+        execute(task)
+
+        when:
+        inputFile.delete()
+
+        then:
+        inputsOutOfDate(task).withRemovedFile(inputFile)
+    }
+
+    def artifactsAreNotUpToDateWhenAnyFileCreatedInInputDir() {
+        given:
+        execute(task)
+
+        when:
+        def file = inputDir.file("other-file").createFile()
+
+        then:
+        inputsOutOfDate(task).withAddedFile(file)
+    }
+
+    def artifactsAreNotUpToDateWhenAnyFileDeletedFromInputDir() {
+        given:
+        execute(task)
+
+        when:
+        inputDirFile.delete()
+
+        then:
+        inputsOutOfDate(task).withRemovedFile(inputDirFile)
+    }
+
+    def artifactsAreNotUpToDateWhenAnyFileInInputDirChangesHash() {
+        given:
+        execute(task)
+
+        when:
+        inputDirFile.writelns("new content")
+
+        then:
+        inputsOutOfDate(task).withModifiedFile(inputDirFile)
+    }
+
+    def artifactsAreNotUpToDateWhenAnyFileInInputDirChangesType() {
+        given:
+        execute(task)
+
+        when:
+        inputDirFile.delete()
+        inputDirFile.mkdir()
+
+        then:
+        inputsOutOfDate(task).withRemovedFile(inputDirFile)
+    }
+
+    def artifactsAreNotUpToDateWhenAnyInputPropertyValueChanged() {
+        when:
+        execute(builder.withProperty("prop", "original value").task())
+        final inputPropertiesTask = builder.withProperty("prop", "new value").task()
+
+        then:
+        outOfDate inputPropertiesTask
+    }
+
+    def inputPropertyValueCanBeNull() {
+        when:
+        TaskInternal task = builder.withProperty("prop", null).task()
+        execute(task)
+
+        then:
+        upToDate(task)
+    }
+
+    def artifactsAreNotUpToDateWhenAnyInputPropertyAdded() {
+        when:
+        execute(task)
+        final addedPropertyTask = builder.withProperty("prop2", "value").task()
+
+        then:
+        outOfDate addedPropertyTask
+    }
+
+    def artifactsAreNotUpToDateWhenAnyInputPropertyRemoved() {
+        given:
+        execute(builder.withProperty("prop2", "value").task())
+
+        expect:
+        outOfDate task
+    }
+
+    def artifactsAreNotUpToDateWhenStateHasNotBeenUpdated() {
+        when:
+        repository.getStateFor(task)
+
+        then:
+        outOfDate task
+    }
+
+    def artifactsAreNotUpToDateWhenOutputDirWhichUsedToExistHasBeenDeleted() {
+        given:
+        // Output dir already exists before first execution of task
+        outputDirFile.createFile()
+
+        TaskInternal task1 = builder.withOutputFiles(outputDir).createsFiles(outputDirFile).task()
+        TaskInternal task2 = builder.withPath("other").withOutputFiles(outputDir).createsFiles(outputDirFile2).task()
+        
+        when:
+        TaskArtifactState state = repository.getStateFor(task1)
+        state.afterTask()
+        
+        then:
+        !state.upToDate
+        
+        when:
+        outputDir.deleteDir()
+
+        and:
+        // Another task creates dir
+        state = repository.getStateFor(task2)
+
+        then:
+        !state.isUpToDate([])
+        
+        when:
+        task2.execute()
+        state.afterTask()
+        
+        then:
+        // Task should be out-of-date
+        outOfDate task1
+    }
+
+    def artifactsAreUpToDateWhenNothingHasChangedSinceOutputFilesWereGenerated() {
+        given:
+        execute(task)
+
+        expect:
+        repository.getStateFor(task).isUpToDate([])
+        repository.getStateFor(task).isUpToDate([])
+    }
+
+    def artifactsAreUpToDateWhenOutputFileWhichDidNotExistNowExists() {
+        given:
+        execute(task)
+
+        when:
+        missingOutputFile.touch()
+
+        then:
+        upToDate task
+    }
+
+    def artifactsAreUpToDateWhenOutputDirWhichWasEmptyIsNoLongerEmpty() {
+        given:
+        execute(task)
+
+        when:
+        emptyOutputDir.file("some-file").touch()
+
+        then:
+        upToDate task
+    }
+
+    def hasEmptyTaskHistoryWhenTaskHasNeverBeenExecuted() {
+        when:
+        TaskArtifactState state = repository.getStateFor(task)
+        
+        then:
+        state.getExecutionHistory().getOutputFiles().getFiles().isEmpty()
+    }
+
+    def hasTaskHistoryFromPreviousExecution() {
+        given:
+        execute(task)
+
+        when:
+        TaskArtifactState state = repository.getStateFor(task)
+        
+        then:
+        state.getExecutionHistory().getOutputFiles().getFiles() == [outputFile, outputDirFile, outputDirFile2] as Set
+    }
+
+    def multipleTasksCanProduceFilesIntoTheSameOutputDirectory() {
+        when:
+        TaskInternal task1 = task
+        TaskInternal task2 = builder.withPath("other").withOutputFiles(outputDir).createsFiles(outputDir.file("output2")).task()
+        execute(task1, task2)
+
+        then:
+        upToDate task1
+        upToDate task2
+    }
+
+    def multipleTasksCanProduceTheSameFileWithTheSameContents() {
+        when:
+        TaskInternal task1 = builder.withOutputFiles(outputFile).task()
+        TaskInternal task2 = builder.withPath("other").withOutputFiles(outputFile).task()
+        execute(task1, task2)
+
+        then:
+        upToDate task1
+        upToDate task2
+    }
+
+    def multipleTasksCanProduceTheSameEmptyDir() {
+        when:
+        TaskInternal task1 = task
+        TaskInternal task2 = builder.withPath("other").withOutputFiles(outputDir).task()
+        execute(task1, task2)
+
+        then:
+        upToDate task1
+        upToDate task2
+    }
+
+    def doesNotConsiderExistingFilesInOutputDirectoryAsProducedByTask() {
+        when:
+        TestFile otherFile = outputDir.file("other").createFile()
+        execute(task)
+        otherFile.delete()
+
+        then:
+        TaskArtifactState state = repository.getStateFor(task)
+        state.isUpToDate([])
+        !state.getExecutionHistory().getOutputFiles().getFiles().contains(otherFile)
+    }
+
+    def considersExistingFileInOutputDirectoryWhichIsUpdatedByTheTaskAsProducedByTask() {
+        when:
+        TestFile otherFile = outputDir.file("other").createFile()
+        TaskArtifactState state = repository.getStateFor(task)
+
+        then:
+        !state.isUpToDate([])
+
+        when:
+        task.execute()
+        otherFile.write("new content")
+        state.afterTask()
+        otherFile.delete()
+
+        then:
+        def stateAfter = repository.getStateFor(task)
+        !stateAfter.upToDate
+        stateAfter.executionHistory.outputFiles.files.contains(otherFile)
+    }
+
+    def fileIsNoLongerConsideredProducedByTaskOnceItIsDeleted() {
+        given:
+        execute(task)
+
+        outputDirFile.delete()
+        TaskArtifactState state = repository.getStateFor(task)
+        state.afterTask()
+
+        when:
+        outputDirFile.write("ignore me")
+
+        then:
+        def stateAfter = repository.getStateFor(task)
+        stateAfter.isUpToDate([])
+        !stateAfter.executionHistory.outputFiles.files.contains(outputDirFile)
+    }
+
+    def artifactsAreUpToDateWhenTaskDoesNotAcceptAnyInputs() {
+        when:
+        TaskInternal noInputsTask = builder.doesNotAcceptInput().task()
+        execute(noInputsTask)
+
+        then:
+        upToDate noInputsTask
+
+        when:
+        outputDirFile.delete()
+
+        then:
+        outOfDate noInputsTask
+    }
+
+    def artifactsAreUpToDateWhenTaskHasNoInputFiles() {
+        when:
+        TaskInternal noInputFilesTask = builder.withInputFiles().task()
+        execute(noInputFilesTask)
+
+        then:
+        upToDate noInputFilesTask
+    }
+
+    def artifactsAreUpToDateWhenTaskHasNoOutputFiles() {
+        when:
+        TaskInternal noOutputsTask = builder.withOutputFiles().task()
+        execute(noOutputsTask)
+
+        then:
+        upToDate noOutputsTask
+    }
+
+    def taskCanProduceIntoDifferentSetsOfOutputFiles() {
+        when:
+        TestFile outputDir2 = tmpDir.createDir("output-dir-2")
+        TestFile outputDirFile2 = outputDir2.file("output-file-2")
+        TaskInternal task1 = builder.withOutputFiles(outputDir).createsFiles(outputDirFile).task()
+        TaskInternal task2 = builder.withOutputFiles(outputDir2).createsFiles(outputDirFile2).task()
+
+        execute(task1, task2)
+
+        then:
+        def state1 = repository.getStateFor(task1)
+        state1.isUpToDate([])
+        state1.executionHistory.outputFiles.files == [outputDirFile] as Set
+
+        and:
+        def state2 = repository.getStateFor(task2)
+        state2.isUpToDate([])
+        state2.executionHistory.outputFiles.files == [outputDirFile2] as Set
+    }
+
+    private void outOfDate(TaskInternal task) {
+        final state = repository.getStateFor(task)
+        assert !state.upToDate
+        assert !state.inputChanges.incremental
+    }
+
+    def inputsOutOfDate(TaskInternal task) {
+        final state = repository.getStateFor(task)
+        assert !state.upToDate
+
+        final inputChanges = state.inputChanges
+        assert inputChanges.incremental
+
+        final changedFiles = new ChangedFiles()
+        inputChanges.outOfDate(new Action<InputFileDetails>() {
+            void execute(InputFileDetails t) {
+                if (t.added) {
+                    println "Added: " + t.file
+                    changedFiles.added << t.file
+                } else if (t.modified) {
+                    println "Modified: " + t.file
+                    changedFiles.modified << t.file
+                } else {
+                    assert false : "Not a valid change"
+                }
+            }
+        })
+        inputChanges.removed(new Action<InputFileDetails>() {
+            void execute(InputFileDetails t) {
+                println "Removed: " + t.file
+                assert t.removed
+                changedFiles.removed << t.file
+            }
+        })
+
+        return changedFiles
+    }
+
+    private void upToDate(TaskInternal task) {
+        final state = repository.getStateFor(task)
+        assert state.isUpToDate([])
+    }
+
+    private void execute(TaskInternal... tasks) {
+        for (TaskInternal task : tasks) {
+            TaskArtifactState state = repository.getStateFor(task)
+            state.isUpToDate([])
+            task.execute()
+            state.afterTask()
+        }
+    }
+
+    private static class ChangedFiles {
+        def added = []
+        def modified = []
+        def removed = []
+
+        void withAddedFile(File file) {
+            assert added == [file]
+            assert modified == []
+            assert removed == []
+        }
+
+        void withModifiedFile(File file) {
+            assert added == []
+            assert modified == [file]
+            assert removed == []
+        }
+
+        void withRemovedFile(File file) {
+            assert added == []
+            assert modified == []
+            assert removed == [file]
+        }
+    }
+
+    private TaskBuilder getBuilder() {
+        return new TaskBuilder()
+    }
+
+    private class TaskBuilder {
+        private String path = "task"
+        private Collection<? extends File> inputs = inputFiles
+        private Collection<? extends File> outputs = outputFiles
+        private Collection<? extends TestFile> create = createFiles
+        private Class<? extends TaskInternal> type = TaskInternal.class
+        private Map<String, Object> inputProperties = new HashMap<String, Object>(toMap("prop", "value"))
+
+        TaskBuilder withInputFiles(File... inputFiles) {
+            inputs = Arrays.asList(inputFiles)
+            return this
+        }
+
+        TaskBuilder withOutputFiles(File... outputFiles) {
+            outputs = Arrays.asList(outputFiles)
+            return this
+        }
+
+        TaskBuilder createsFiles(TestFile... outputFiles) {
+            create = Arrays.asList(outputFiles)
+            return this
+        }
+
+        TaskBuilder withPath(String path) {
+            this.path = path
+            return this
+        }
+
+        TaskBuilder withType(Class<? extends TaskInternal> type) {
+            this.type = type
+            return this
+        }
+
+        TaskBuilder doesNotAcceptInput() {
+            inputs = null
+            inputProperties = null
+            return this
+        }
+
+        public TaskBuilder withProperty(String name, Object value) {
+            inputProperties.put(name, value)
+            return this
+        }
+
+        TaskInternal task() {
+            final TaskInternal task = TestUtil.createTask(type, project, path)
+            if (inputs != null) {
+                task.getInputs().files(inputs)
+            }
+            if (inputProperties != null) {
+                task.getInputs().properties(inputProperties)
+            }
+            if (outputs != null) {
+                task.getOutputs().files(outputs)
+            }
+            task.doLast(new org.gradle.api.Action<Object>() {
+                public void execute(Object o) {
+                    for (TestFile file : create) {
+                        file.createFile()
+                    }
+                }
+            })
+
+            return task
+        }
+    }
+
+    public static class TaskSubType extends DefaultTask {
+    }
+
+}
diff --git a/subprojects/core/src/test/groovy/org/gradle/api/internal/changedetection/changes/ShortCircuitTaskArtifactStateRepositoryTest.groovy b/subprojects/core/src/test/groovy/org/gradle/api/internal/changedetection/changes/ShortCircuitTaskArtifactStateRepositoryTest.groovy
new file mode 100755
index 0000000..86723cb
--- /dev/null
+++ b/subprojects/core/src/test/groovy/org/gradle/api/internal/changedetection/changes/ShortCircuitTaskArtifactStateRepositoryTest.groovy
@@ -0,0 +1,112 @@
+/*
+ * Copyright 2010 the original author or authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY 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.changes
+
+import org.gradle.StartParameter
+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.specs.Spec
+import org.gradle.internal.reflect.DirectInstantiator
+import spock.lang.Specification
+
+public class ShortCircuitTaskArtifactStateRepositoryTest extends Specification {
+    StartParameter startParameter = new StartParameter()
+    TaskArtifactStateRepository delegate = Mock(TaskArtifactStateRepository)
+    ShortCircuitTaskArtifactStateRepository repository = new ShortCircuitTaskArtifactStateRepository(startParameter, new DirectInstantiator(), delegate)
+    TaskArtifactState taskArtifactState = Mock(TaskArtifactState)
+    TaskInternal task = Mock(TaskInternal)
+    TaskOutputsInternal outputs = Mock(TaskOutputsInternal)
+    Spec upToDateSpec = Mock(Spec)
+
+    def doesNotLoadHistoryWhenTaskHasNoDeclaredOutputs() {
+        def messages = []
+
+        when:
+        TaskArtifactState state = repository.getStateFor(task);
+
+        then:
+        1 * task.getOutputs() >> outputs
+        1 * outputs.getHasOutput() >> false
+        0 * _
+
+        and:
+        state instanceof NoHistoryArtifactState
+        !state.isUpToDate(messages)
+        !messages.empty
+    }
+
+    def delegatesDirectToBackingRepositoryWithoutRerunTasks() {
+        when:
+        TaskArtifactState state = repository.getStateFor(task);
+
+        then:
+        2 * task.getOutputs() >> outputs
+        1 * outputs.getHasOutput() >> true
+        1 * outputs.getUpToDateSpec() >> upToDateSpec
+        1 * upToDateSpec.isSatisfiedBy(task) >> true
+
+        and:
+        1 * delegate.getStateFor(task) >> taskArtifactState
+        state == taskArtifactState
+    }
+
+    def taskArtifactsAreAlwaysOutOfDateWithRerunTasks() {
+        def messages = []
+
+        when:
+        startParameter.setRerunTasks(true);
+        def state = repository.getStateFor(task)
+
+        then:
+        1 * task.getOutputs() >> outputs
+        1 * outputs.getHasOutput() >> true
+        1 * delegate.getStateFor(task) >> taskArtifactState
+        0 * taskArtifactState._
+
+        and:
+        !state.isUpToDate(messages)
+        !messages.empty
+
+        and:
+        !state.inputChanges.incremental
+    }
+
+    def taskArtifactsAreAlwaysOutOfDateWhenUpToDateSpecReturnsFalse() {
+        def messages = []
+
+        when:
+        def state = repository.getStateFor(task)
+
+        then:
+        2 * task.getOutputs() >> outputs
+        1 * outputs.getHasOutput() >> true
+        1 * outputs.getUpToDateSpec() >> upToDateSpec
+        1 * upToDateSpec.isSatisfiedBy(task) >> false
+
+        and:
+        1 * delegate.getStateFor(task) >> taskArtifactState
+        0 * taskArtifactState._
+
+        and:
+        !state.isUpToDate(messages)
+        !messages.empty
+
+        and:
+        !state.inputChanges.incremental
+    }
+}
diff --git a/subprojects/core/src/test/groovy/org/gradle/api/internal/changedetection/rules/CachingTaskStateChangesTest.groovy b/subprojects/core/src/test/groovy/org/gradle/api/internal/changedetection/rules/CachingTaskStateChangesTest.groovy
new file mode 100644
index 0000000..3acfff7
--- /dev/null
+++ b/subprojects/core/src/test/groovy/org/gradle/api/internal/changedetection/rules/CachingTaskStateChangesTest.groovy
@@ -0,0 +1,84 @@
+/*
+ * Copyright 2013 the original author or authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY 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.rules;
+
+import spock.lang.Specification;
+
+public class CachingTaskStateChangesTest extends Specification {
+    def delegate = Mock(TaskStateChanges)
+    def change1 = Mock(TaskStateChange)
+    def change2 = Mock(TaskStateChange)
+    def change3 = Mock(TaskStateChange)
+
+    def cachingChanges = new CachingTaskStateChanges(2, delegate)
+
+    def "delegates to underlying instance"() {
+        when:
+        def reported = cachingChanges.iterator().collect()
+
+        then:
+        delegate.iterator() >> [change1, change2, change3].iterator()
+
+        and:
+        reported == [change1, change2, change3]
+
+        when:
+        cachingChanges.snapshotAfterTask()
+
+        then:
+        delegate.snapshotAfterTask()
+    }
+
+    def "caches all reported changes under cache size"() {
+        when:
+        cachingChanges.iterator().collect()
+
+        then:
+        1 * delegate.iterator() >> [change1, change2].iterator()
+        0 * _
+
+        when:
+        cachingChanges.iterator().collect()
+        def reported = cachingChanges.iterator().collect()
+
+        then:
+        0 * _
+
+        and:
+        reported == [change1, change2]
+    }
+
+    def "does not cache once reported changes exceed cache size"() {
+        when:
+        cachingChanges.iterator().collect()
+
+        then:
+        1 * delegate.iterator() >> [change1, change2, change3].iterator()
+        0 * _
+
+        when:
+        cachingChanges.iterator().collect()
+        def reported = cachingChanges.iterator().collect()
+
+        then:
+        2 * delegate.iterator() >> [change1, change2, change3].iterator() >> [change3, change2, change1].iterator()
+        0 * _
+
+        and:
+        reported == [change3, change2, change1]
+    }
+}
diff --git a/subprojects/core/src/test/groovy/org/gradle/api/internal/changedetection/rules/InputFilesStateChangeRuleTest.groovy b/subprojects/core/src/test/groovy/org/gradle/api/internal/changedetection/rules/InputFilesStateChangeRuleTest.groovy
new file mode 100644
index 0000000..6bad53c
--- /dev/null
+++ b/subprojects/core/src/test/groovy/org/gradle/api/internal/changedetection/rules/InputFilesStateChangeRuleTest.groovy
@@ -0,0 +1,78 @@
+/*
+ * Copyright 2013 the original author or authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY 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.rules
+import org.gradle.api.internal.TaskInternal
+import org.gradle.api.internal.changedetection.state.FileCollectionSnapshot
+import org.gradle.api.internal.changedetection.state.FileSnapshotter
+import org.gradle.api.internal.changedetection.state.TaskExecution
+import org.gradle.api.internal.file.collections.SimpleFileCollection
+import org.gradle.api.tasks.TaskInputs
+import org.gradle.util.ChangeListener
+import spock.lang.Specification
+
+public class InputFilesStateChangeRuleTest extends Specification {
+    def inputSnapshot = Mock(FileCollectionSnapshot)
+    def previousInputSnapshot = Mock(FileCollectionSnapshot)
+    FileCollectionSnapshot.ChangeIterator<String> changeIterator = Mock()
+
+    TaskStateChanges createStateChanges() {
+        def taskInputs = Stub(TaskInputs) {
+            getFiles() >> new SimpleFileCollection()
+        }
+        def task = Stub(TaskInternal) {
+            getInputs() >> taskInputs
+        }
+        def snapshotter = Stub(FileSnapshotter) {
+            snapshot(_) >> inputSnapshot
+        }
+
+        def previousExecution = Stub(TaskExecution) {
+            getInputFilesSnapshot() >> previousInputSnapshot
+        }
+        return InputFilesStateChangeRule.create(task, previousExecution, Mock(TaskExecution), snapshotter)
+    }
+
+    def "emits change for no previous input snapshot"() {
+        when:
+        previousInputSnapshot = null
+        def messages = createStateChanges().iterator().collect {it.message}
+
+        then:
+        messages == ["Input file history is not available."]
+    }
+
+    def "emits change for file changes since previous input snapshot"() {
+        when:
+        def messages = createStateChanges().iterator().collect {it.message}
+
+        then:
+        1 * inputSnapshot.iterateChangesSince(previousInputSnapshot) >> changeIterator
+        4 * changeIterator.next(_ as ChangeListener) >> { ChangeListener listener ->
+            listener.added("one")
+            true
+        } >> { ChangeListener listener ->
+            listener.removed("two")
+            true
+        } >> { ChangeListener listener ->
+            listener.changed("three")
+            true
+        } >> false
+
+        and:
+        messages == ["Input file one has been added.", "Input file two has been removed.", "Input file three has changed."]
+    }
+}
diff --git a/subprojects/core/src/test/groovy/org/gradle/api/internal/changedetection/rules/OutputFilesStateChangeRuleTest.groovy b/subprojects/core/src/test/groovy/org/gradle/api/internal/changedetection/rules/OutputFilesStateChangeRuleTest.groovy
new file mode 100644
index 0000000..cbcfe75
--- /dev/null
+++ b/subprojects/core/src/test/groovy/org/gradle/api/internal/changedetection/rules/OutputFilesStateChangeRuleTest.groovy
@@ -0,0 +1,82 @@
+/*
+ * Copyright 2013 the original author or authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY 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.rules
+
+import org.gradle.api.internal.TaskInternal
+import org.gradle.api.internal.TaskOutputsInternal
+import org.gradle.api.internal.changedetection.state.FileCollectionSnapshot
+import org.gradle.api.internal.changedetection.state.FileSnapshotter
+import org.gradle.api.internal.changedetection.state.TaskExecution
+import org.gradle.api.internal.file.collections.SimpleFileCollection
+import org.gradle.util.ChangeListener
+import spock.lang.Specification;
+
+public class OutputFilesStateChangeRuleTest extends Specification {
+    def outputSnapshot = Mock(FileCollectionSnapshot)
+    def previousOutputSnapshot = Mock(FileCollectionSnapshot)
+
+    TaskStateChanges createStateChanges() {
+        def taskOutputs = Stub(TaskOutputsInternal) {
+            getFiles() >> new SimpleFileCollection()
+        }
+        def task = Stub(TaskInternal) {
+            getOutputs() >> taskOutputs
+        }
+        def snapshotter = Stub(FileSnapshotter) {
+            snapshot(_) >> outputSnapshot
+        }
+
+        def previousExecution = Stub(TaskExecution) {
+            getOutputFilesSnapshot() >> previousOutputSnapshot
+        }
+        return OutputFilesStateChangeRule.create(task, previousExecution, Mock(TaskExecution), snapshotter)
+    }
+
+    def "emits change for no previous output snapshot"() {
+        when:
+        previousOutputSnapshot = null
+        def it = createStateChanges().iterator()
+
+        then:
+        it.hasNext()
+        it.next().message == "Output file history is not available."
+        !it.hasNext()
+    }
+
+    def "emits change for file changes since previous output snapshot"() {
+        FileCollectionSnapshot.ChangeIterator<String> changeIterator = Mock()
+        when:
+        def it = createStateChanges().iterator()
+        def messages = it.collect {it.message}
+
+        then:
+        1 * outputSnapshot.iterateChangesSince(previousOutputSnapshot) >> changeIterator
+        4 * changeIterator.next(_ as ChangeListener<String>) >> { ChangeListener listener ->
+            listener.added("one")
+            true
+        } >> { ChangeListener listener ->
+            listener.removed("two")
+            true
+        } >> { ChangeListener listener ->
+            listener.changed("three")
+            true
+        } >> false
+
+        and:
+        messages == ["Output file one has been added.", "Output file two has been removed.", "Output file three has changed."]
+    }
+}
diff --git a/subprojects/core/src/test/groovy/org/gradle/api/internal/changedetection/rules/SimpleTaskStateChangesTest.groovy b/subprojects/core/src/test/groovy/org/gradle/api/internal/changedetection/rules/SimpleTaskStateChangesTest.groovy
new file mode 100644
index 0000000..5c89d6f
--- /dev/null
+++ b/subprojects/core/src/test/groovy/org/gradle/api/internal/changedetection/rules/SimpleTaskStateChangesTest.groovy
@@ -0,0 +1,56 @@
+/*
+ * Copyright 2013 the original author or authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY 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.rules
+
+import spock.lang.Specification;
+
+public class SimpleTaskStateChangesTest extends Specification {
+    def simpleTaskStateChanges = new TestSimpleTaskStateChanges()
+    def change1 = Mock(TaskStateChange)
+    def change2 = Mock(TaskStateChange)
+
+    def "fires all changes"() {
+        when:
+        final iterator = simpleTaskStateChanges.iterator()
+
+        then:
+        iterator.hasNext()
+        iterator.next() == change1
+        iterator.hasNext()
+        iterator.next() == change2
+    }
+
+    def "caches all changes"() {
+        when:
+        simpleTaskStateChanges.iterator().next()
+        simpleTaskStateChanges.iterator().next()
+
+        then:
+        simpleTaskStateChanges.addAllCount == 1
+    }
+
+    private class TestSimpleTaskStateChanges extends SimpleTaskStateChanges {
+        int addAllCount;
+        @Override
+        protected void addAllChanges(List<TaskStateChange> changes) {
+            changes.addAll([change1, change2])
+            addAllCount++
+        }
+    }
+}
diff --git a/subprojects/core/src/test/groovy/org/gradle/api/internal/changedetection/rules/SummaryTaskStateChangesTest.groovy b/subprojects/core/src/test/groovy/org/gradle/api/internal/changedetection/rules/SummaryTaskStateChangesTest.groovy
new file mode 100644
index 0000000..1fb1171
--- /dev/null
+++ b/subprojects/core/src/test/groovy/org/gradle/api/internal/changedetection/rules/SummaryTaskStateChangesTest.groovy
@@ -0,0 +1,89 @@
+/*
+ * Copyright 2013 the original author or authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY 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.rules
+
+import spock.lang.Specification
+
+class SummaryTaskStateChangesTest extends Specification {
+
+    def state1 = Mock(TaskStateChanges)
+    def state2 = Mock(TaskStateChanges)
+    def state = new SummaryTaskStateChanges(2, state1, state2)
+    def change = Mock(TaskStateChange)
+
+    def looksForChangesInAllDelegateChangeSets() {
+        when:
+        def hasNext = state.iterator().hasNext()
+
+        then:
+        1 * state1.iterator() >> [].iterator()
+        1 * state2.iterator() >> [].iterator()
+        0 * _
+
+        and:
+        !hasNext
+    }
+
+    def delegatesSnapshotToAllDelegateChangeSets() {
+        when:
+        state.snapshotAfterTask()
+
+        then:
+        1 * state1.snapshotAfterTask()
+        1 * state2.snapshotAfterTask()
+        0 * _
+    }
+
+    def onlyReturnsChangesFromASingleDelegate() {
+        def change1 = Mock(TaskStateChange)
+
+        when:
+        def it = state.iterator()
+        it.hasNext()
+
+        then:
+        1 * state1.iterator() >> [change1].iterator()
+        0 * _
+
+        and:
+        it.hasNext()
+        it.next() == change1
+        !it.hasNext()
+    }
+
+    def willNotEmitMoreChangesThanSpecified() {
+        def change1 = Mock(TaskStateChange)
+        def change2 = Mock(TaskStateChange)
+        def change3 = Mock(TaskStateChange)
+
+        when:
+        def it = state.iterator()
+        it.hasNext()
+
+        then:
+        1 * state1.iterator() >> [].iterator()
+        1 * state2.iterator() >> [change1, change2, change3].iterator()
+
+        and:
+        it.hasNext()
+        it.next() == change1
+        it.hasNext()
+        it.next() == change2
+        !it.hasNext()
+    }
+}
diff --git a/subprojects/core/src/test/groovy/org/gradle/api/internal/changedetection/state/CacheBackedFileSnapshotRepositoryTest.groovy b/subprojects/core/src/test/groovy/org/gradle/api/internal/changedetection/state/CacheBackedFileSnapshotRepositoryTest.groovy
new file mode 100644
index 0000000..0627bb8
--- /dev/null
+++ b/subprojects/core/src/test/groovy/org/gradle/api/internal/changedetection/state/CacheBackedFileSnapshotRepositoryTest.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.api.internal.changedetection.state
+
+import org.gradle.cache.PersistentIndexedCache
+import org.gradle.internal.id.IdGenerator
+import spock.lang.Specification
+
+class CacheBackedFileSnapshotRepositoryTest extends Specification {
+    final TaskArtifactStateCacheAccess cacheAccess = Mock()
+    final PersistentIndexedCache<Object, Object> indexedCache = Mock()
+    final IdGenerator<Long> idGenerator = Mock()
+    FileSnapshotRepository repository
+
+    def setup() {
+        1 * cacheAccess.createCache("fileSnapshots", _, _, _) >> indexedCache
+        repository = new CacheBackedFileSnapshotRepository(cacheAccess, idGenerator)
+    }
+
+    def "assigns an id when a snapshot is added"() {
+        FileCollectionSnapshot snapshot = Mock()
+
+        when:
+        def id = repository.add(snapshot)
+
+        then:
+        id == 15
+        1 * idGenerator.generateId() >> 15L
+        1 * indexedCache.put(15, snapshot)
+        0 * _._
+    }
+
+    def "can fetch a snapshot by id"() {
+        FileCollectionSnapshot snapshot = Mock()
+
+        when:
+        def result = repository.get(4)
+
+        then:
+        result == snapshot
+        1 * indexedCache.get(4) >> snapshot
+        0 * _._
+    }
+
+    def "can delete a snapshot by id"() {
+        when:
+        repository.remove(4)
+
+        then:
+        1 * indexedCache.remove(4)
+        0 * _._
+    }
+}
diff --git a/subprojects/core/src/test/groovy/org/gradle/api/internal/changedetection/state/CachingHasherTest.java b/subprojects/core/src/test/groovy/org/gradle/api/internal/changedetection/state/CachingHasherTest.java
new file mode 100644
index 0000000..b44c277
--- /dev/null
+++ b/subprojects/core/src/test/groovy/org/gradle/api/internal/changedetection/state/CachingHasherTest.java
@@ -0,0 +1,108 @@
+/*
+ * Copyright 2010 the original author or authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY 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.state;
+
+import org.gradle.cache.PersistentIndexedCache;
+import org.gradle.messaging.serialize.Serializer;
+import org.gradle.test.fixtures.file.TestNameTestDirectoryProvider;
+import org.jmock.Expectations;
+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 static org.gradle.util.Matchers.reflectionEquals;
+import static org.hamcrest.Matchers.*;
+import static org.junit.Assert.assertThat;
+
+ at RunWith(JMock.class)
+public class CachingHasherTest {
+    @Rule
+    TestNameTestDirectoryProvider tmpDir = new TestNameTestDirectoryProvider();
+    private final JUnit4Mockery context = new JUnit4Mockery();
+    private final Hasher delegate = context.mock(Hasher.class);
+    private final PersistentIndexedCache<File, CachingHasher.FileInfo> cache = context.mock(
+            PersistentIndexedCache.class);
+    private final TaskArtifactStateCacheAccess cacheAccess = context.mock(TaskArtifactStateCacheAccess.class);
+    private final byte[] hash = "hash".getBytes();
+    private final File file = tmpDir.createFile("testfile").write("content");
+    private CachingHasher hasher;
+
+    @Before
+    public void setup() {
+        context.checking(new Expectations(){{
+            one(cacheAccess).createCache(with(equalTo("fileHashes")), with(equalTo(File.class)), with(notNullValue(Class.class)), with(notNullValue(Serializer.class)));
+            will(returnValue(cache));
+        }});
+        hasher = new CachingHasher(delegate, cacheAccess);
+    }
+
+    @Test
+    public void hashesFileWhenHashNotCached() {
+        context.checking(new Expectations() {{
+            one(cache).get(file);
+            will(returnValue(null));
+            one(delegate).hash(file);
+            will(returnValue(hash));
+            one(cache).put(with(equalTo(file)), with(reflectionEquals(new CachingHasher.FileInfo(hash, file.length(),
+                    file.lastModified()))));
+        }});
+
+        assertThat(hasher.hash(file), sameInstance(hash));
+    }
+
+    @Test
+    public void hashesFileWhenLengthHasChanged() {
+        context.checking(new Expectations() {{
+            one(cache).get(file);
+            will(returnValue(new CachingHasher.FileInfo(hash, 1078, file.lastModified())));
+            one(delegate).hash(file);
+            will(returnValue(hash));
+            one(cache).put(with(equalTo(file)), with(reflectionEquals(new CachingHasher.FileInfo(hash, file.length(),
+                    file.lastModified()))));
+        }});
+
+        assertThat(hasher.hash(file), sameInstance(hash));
+    }
+
+    @Test
+    public void hashesFileWhenTimestampHasChanged() {
+        context.checking(new Expectations() {{
+            one(cache).get(file);
+            will(returnValue(new CachingHasher.FileInfo(hash, file.length(), 12)));
+            one(delegate).hash(file);
+            will(returnValue(hash));
+            one(cache).put(with(equalTo(file)), with(reflectionEquals(new CachingHasher.FileInfo(hash, file.length(),
+                    file.lastModified()))));
+        }});
+
+        assertThat(hasher.hash(file), sameInstance(hash));
+    }
+
+    @Test
+    public void doesNotHashFileWhenTimestampAndLengthHaveNotChanged() {
+        context.checking(new Expectations() {{
+            one(cache).get(file);
+            will(returnValue(new CachingHasher.FileInfo(hash, file.length(), file.lastModified())));
+        }});
+
+        assertThat(hasher.hash(file), sameInstance(hash));
+    }
+}
diff --git a/subprojects/core/src/test/groovy/org/gradle/api/internal/changedetection/state/DefaultFileSnapshotterSerializerTest.groovy b/subprojects/core/src/test/groovy/org/gradle/api/internal/changedetection/state/DefaultFileSnapshotterSerializerTest.groovy
new file mode 100644
index 0000000..c7779e5
--- /dev/null
+++ b/subprojects/core/src/test/groovy/org/gradle/api/internal/changedetection/state/DefaultFileSnapshotterSerializerTest.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.api.internal.changedetection.state
+
+import org.gradle.messaging.serialize.SerializerSpec
+
+class DefaultFileSnapshotterSerializerTest extends SerializerSpec {
+
+    def serializer = new DefaultFileSnapshotterSerializer()
+
+    def "reads and writes the snapshot"() {
+        when:
+        DefaultFileSnapshotter.FileCollectionSnapshotImpl out = serialize(new DefaultFileSnapshotter.FileCollectionSnapshotImpl([
+                "1": new DefaultFileSnapshotter.DirSnapshot(),
+                "2": new DefaultFileSnapshotter.MissingFileSnapshot(),
+                "3": new DefaultFileSnapshotter.FileHashSnapshot("foo".bytes)]), serializer)
+
+        then:
+        out.snapshots.size() == 3
+        out.snapshots['1'] instanceof DefaultFileSnapshotter.DirSnapshot
+        out.snapshots['2'] instanceof DefaultFileSnapshotter.MissingFileSnapshot
+        ((DefaultFileSnapshotter.FileHashSnapshot) out.snapshots['3']).hash == "foo".bytes
+    }
+}
diff --git a/subprojects/core/src/test/groovy/org/gradle/api/internal/changedetection/state/DefaultFileSnapshotterTest.groovy b/subprojects/core/src/test/groovy/org/gradle/api/internal/changedetection/state/DefaultFileSnapshotterTest.groovy
new file mode 100755
index 0000000..170868d
--- /dev/null
+++ b/subprojects/core/src/test/groovy/org/gradle/api/internal/changedetection/state/DefaultFileSnapshotterTest.groovy
@@ -0,0 +1,346 @@
+/*
+ * Copyright 2010 the original author or authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY 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.state
+
+import org.gradle.api.file.FileCollection
+import org.gradle.api.file.FileTree
+import org.gradle.test.fixtures.file.TestFile
+import org.gradle.test.fixtures.file.TestNameTestDirectoryProvider
+import org.gradle.util.ChangeListener
+import org.junit.Rule
+import spock.lang.Specification
+
+public class DefaultFileSnapshotterTest extends Specification {
+    def hasher = new DefaultHasher()
+    def cacheAccess = Stub(TaskArtifactStateCacheAccess)
+    def snapshotter = new DefaultFileSnapshotter(hasher, cacheAccess)
+
+    def listener = Mock(ChangeListener)
+    @Rule
+    public final TestNameTestDirectoryProvider tmpDir = new TestNameTestDirectoryProvider()
+
+    def setup() {
+        cacheAccess.useCache(_, _) >> { args ->
+            args[1].run()
+        }
+    }
+
+    def getFilesReturnsOnlyTheFilesWhichExisted() {
+        given:
+        TestFile file = tmpDir.createFile('file1')
+        TestFile dir = tmpDir.createDir('file2')
+        TestFile noExist = tmpDir.file('file3')
+
+        when:
+        def snapshot = snapshotter.snapshot(files(file, dir, noExist))
+
+        then:
+        snapshot.files.files as List == [file]
+    }
+    
+    def notifiesListenerWhenFileAdded() {
+        given:
+        TestFile file1 = tmpDir.createFile('file1')
+        TestFile file2 = tmpDir.createFile('file2')
+
+        when:
+        def snapshot = snapshotter.snapshot(files(file1))
+        snapshotter.snapshot(files(file1, file2)).iterateChangesSince(snapshot).next(listener)
+
+        then:
+        1 * listener.added(file2.path)
+        0 * _
+    }
+
+    def notifiesListenerWhenFileRemoved() {
+        given:
+        TestFile file1 = tmpDir.createFile('file1')
+        TestFile file2 = tmpDir.createFile('file2')
+
+        when:
+        FileCollectionSnapshot snapshot = snapshotter.snapshot(files(file1, file2))
+        snapshotter.snapshot(files(file1)).iterateChangesSince(snapshot).next(listener)
+
+        then:
+        1 * listener.removed(file2.path)
+        0 * _
+    }
+
+    def fileHasNotChangedWhenTypeAndHashHaveNotChanged() {
+        given:
+        TestFile file = tmpDir.createFile('file')
+
+        when:
+        FileCollectionSnapshot snapshot = snapshotter.snapshot(files(file))
+        snapshotter.snapshot(files(file)).iterateChangesSince(snapshot).next(listener)
+
+        then:
+        _ * listener.stopped >> false
+        _ * listener.resumeAfter >> null
+        0 * listener._
+    }
+
+    def fileHasChangedWhenTypeHasChanged() {
+        given:
+        TestFile file = tmpDir.createFile('file')
+
+        when:
+        FileCollectionSnapshot snapshot = snapshotter.snapshot(files(file))
+        file.delete()
+        file.createDir()
+        snapshotter.snapshot(files(file)).iterateChangesSince(snapshot).next(listener)
+
+        then:
+        1 * listener.changed(file.path)
+
+        and:
+        _ * listener.stopped >> false
+        _ * listener.resumeAfter >> null
+        0 * _
+    }
+
+    def fileHasChangedWhenHashHasChanged() {
+        TestFile file = tmpDir.createFile('file')
+
+        when:
+        FileCollectionSnapshot snapshot = snapshotter.snapshot(files(file))
+        file.write('new content')
+        snapshotter.snapshot(files(file)).iterateChangesSince(snapshot).next(listener)
+
+        then:
+        1 * listener.changed(file.path)
+
+        and:
+        _ * listener.stopped >> false
+        _ * listener.resumeAfter >> null
+        0 * _
+    }
+
+    def directoryHasNotChangedWhenTypeHasNotChanged() {
+        TestFile dir = tmpDir.createDir('dir')
+
+        when:
+        FileCollectionSnapshot snapshot = snapshotter.snapshot(files(dir))
+
+        snapshotter.snapshot(files(dir)).iterateChangesSince(snapshot).next(listener)
+
+        then:
+        _ * listener.stopped >> false
+        _ * listener.resumeAfter >> null
+        0 * _
+    }
+
+    def directoryHasChangedWhenTypeHasChanged() {
+        TestFile dir = tmpDir.createDir('dir')
+
+        when:
+        FileCollectionSnapshot snapshot = snapshotter.snapshot(files(dir))
+        dir.deleteDir()
+        dir.createFile()
+        snapshotter.snapshot(files(dir)).iterateChangesSince(snapshot).next(listener)
+
+        then:
+        1 * listener.changed(dir.path)
+    }
+
+    def nonExistentFileUnchangedWhenTypeHasNotChanged() {
+        TestFile file = tmpDir.file('unknown')
+
+        when:
+        FileCollectionSnapshot snapshot = snapshotter.snapshot(files(file))
+        snapshotter.snapshot(files(file)).iterateChangesSince(snapshot).next(listener)
+
+        then:
+        _ * listener.stopped >> false
+        _ * listener.resumeAfter >> null
+        0 * _
+    }
+
+    def nonExistentFileIsChangedWhenTypeHasChanged() {
+        TestFile file = tmpDir.file('unknown')
+
+        when:
+        FileCollectionSnapshot snapshot = snapshotter.snapshot(files(file))
+        file.createFile()
+        snapshotter.snapshot(files(file)).iterateChangesSince(snapshot).next(listener)
+
+        then:
+        1 * listener.changed(file.path)
+    }
+
+    def ignoresDuplicatesInFileCollection() {
+        TestFile file1 = tmpDir.createFile('file')
+        TestFile file2 = tmpDir.createFile('file')
+
+        when:
+        FileCollectionSnapshot snapshot = snapshotter.snapshot(files(file1, file2))
+        snapshotter.snapshot(files(file1)).iterateChangesSince(snapshot).next(listener)
+
+        then:
+        _ * listener.stopped >> false
+        _ * listener.resumeAfter >> null
+        0 * _
+    }
+
+    def canCreateEmptySnapshot() {
+        TestFile file = tmpDir.createFile('file')
+
+        when:
+        FileCollectionSnapshot snapshot = snapshotter.emptySnapshot()
+        FileCollectionSnapshot newSnapshot = snapshotter.snapshot(files(file))
+        newSnapshot.iterateChangesSince(snapshot).next(listener)
+
+        then:
+        1 * listener.added(file.path)
+    }
+
+    def diffAddsAddedFilesToSnapshot() {
+        TestFile file = tmpDir.createFile('file')
+
+        given:
+        ChangeListener<FileCollectionSnapshot.Merge> mergeListener = Mock(ChangeListener.class)
+
+        FileCollectionSnapshot original = snapshotter.emptySnapshot()
+        FileCollectionSnapshot modified = snapshotter.snapshot(files(file))
+
+        when:
+        FileCollectionSnapshot target = modified.changesSince(original).applyTo(snapshotter.emptySnapshot(), mergeListener)
+
+        then:
+        1 * mergeListener.added(_)
+
+        when:
+        target.iterateChangesSince(original).next(listener)
+
+        then:
+        1 * listener.added(file.path)
+    }
+
+    def canIgnoreAddedFileInDiff() {
+        TestFile file = tmpDir.createFile('file')
+
+        ChangeListener<FileCollectionSnapshot.Merge> mergeListener = Mock(ChangeListener.class)
+        FileCollectionSnapshot original = snapshotter.emptySnapshot()
+        FileCollectionSnapshot modified = snapshotter.snapshot(files(file))
+
+        when:
+        FileCollectionSnapshot target = modified.changesSince(original).applyTo(snapshotter.emptySnapshot(), mergeListener)
+        target.iterateChangesSince(original).next(listener)
+
+        then:
+        mergeListener.added(!null) >> { FileCollectionSnapshot.Merge merge -> merge.ignore() }
+    }
+
+    def diffAddsChangedFilesToSnapshot() {
+        TestFile file = tmpDir.createFile('file')
+
+        ChangeListener<FileCollectionSnapshot.Merge> mergeListener = Mock(ChangeListener.class)
+
+        FileCollectionSnapshot original = snapshotter.snapshot(files(file))
+        file.write('new content')
+        FileCollectionSnapshot modified = snapshotter.snapshot(files(file))
+
+        when:
+        FileCollectionSnapshot target = modified.changesSince(original).applyTo(snapshotter.emptySnapshot(), mergeListener)
+
+        then:
+        1 * mergeListener.changed(!null)
+
+        when:
+        target.iterateChangesSince(original).next(listener)
+
+        then:
+        1 * listener.changed(file.path)
+    }
+
+    def canIgnoreChangedFileInDiff() {
+        TestFile file = tmpDir.createFile('file')
+        ChangeListener<FileCollectionSnapshot.Merge> mergeListener = Mock(ChangeListener.class)
+
+        when:
+        FileCollectionSnapshot original = snapshotter.snapshot(files(file))
+        FileCollectionSnapshot target = snapshotter.snapshot(files(file))
+        file.write('new content')
+        FileCollectionSnapshot modified = snapshotter.snapshot(files(file))
+
+        and:
+        target = modified.changesSince(original).applyTo(target, mergeListener)
+        target.iterateChangesSince(original).next(listener)
+
+        then:
+        1 * mergeListener.changed(!null) >> { FileCollectionSnapshot.Merge merge -> merge.ignore() }
+    }
+
+    def diffRemovesDeletedFilesFromSnapshot() {
+        TestFile file = tmpDir.createFile('file')
+        ChangeListener<FileCollectionSnapshot.Merge> mergeListener = Mock(ChangeListener.class)
+
+        when:
+        FileCollectionSnapshot original = snapshotter.snapshot(files(file))
+        FileCollectionSnapshot modified = snapshotter.emptySnapshot()
+
+        FileCollectionSnapshot target = modified.changesSince(original).applyTo(snapshotter.snapshot(files(file)), mergeListener)
+
+        then:
+        1 * mergeListener.removed(!null)
+
+        when:
+        target.iterateChangesSince(original).next(listener)
+
+        then:
+        1 * listener.removed(file.path)
+    }
+
+    def canIgnoreRemovedFileInDiff() {
+        TestFile file = tmpDir.createFile('file')
+        ChangeListener<FileCollectionSnapshot.Merge> mergeListener = Mock(ChangeListener.class)
+
+        when:
+        FileCollectionSnapshot original = snapshotter.snapshot(files(file))
+        FileCollectionSnapshot modified = snapshotter.emptySnapshot()
+        FileCollectionSnapshot target = modified.changesSince(original).applyTo(snapshotter.snapshot(files(file)), mergeListener)
+
+        target.iterateChangesSince(original).next(listener)
+
+        then:
+        mergeListener.removed(!null) >> { FileCollectionSnapshot.Merge merge -> merge.ignore() }
+    }
+
+    def diffIgnoresUnchangedFilesInSnapshot() {
+        TestFile file = tmpDir.createFile('file')
+        ChangeListener<FileCollectionSnapshot.Merge> mergeListener = Mock(ChangeListener.class)
+
+        when:
+        FileCollectionSnapshot original = snapshotter.snapshot(files(file))
+        FileCollectionSnapshot modified = snapshotter.snapshot(files(file))
+        FileCollectionSnapshot target = modified.changesSince(original).applyTo(snapshotter.emptySnapshot(), mergeListener)
+
+        target.iterateChangesSince(snapshotter.emptySnapshot()).next(listener)
+
+        then:
+        _ * listener.stopped >> false
+        _ * listener.resumeAfter >> null
+        0 * _
+    }
+
+    private FileCollection files(File... files) {
+        FileTree collection = Mock(FileTree.class)
+        _ * collection.asFileTree >> collection
+        _ * collection.getFiles() >> files
+        return collection
+    }
+    
+}
diff --git a/subprojects/core/src/test/groovy/org/gradle/api/internal/changedetection/state/DefaultTaskArtifactStateCacheAccessTest.groovy b/subprojects/core/src/test/groovy/org/gradle/api/internal/changedetection/state/DefaultTaskArtifactStateCacheAccessTest.groovy
new file mode 100644
index 0000000..390150c
--- /dev/null
+++ b/subprojects/core/src/test/groovy/org/gradle/api/internal/changedetection/state/DefaultTaskArtifactStateCacheAccessTest.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.changedetection.state
+
+import org.gradle.api.internal.GradleInternal
+import org.gradle.cache.CacheRepository
+import org.gradle.cache.DirectoryCacheBuilder
+import org.gradle.cache.PersistentCache
+import org.gradle.cache.PersistentIndexedCache
+import org.gradle.messaging.serialize.DefaultSerializer
+import spock.lang.Specification
+
+class DefaultTaskArtifactStateCacheAccessTest extends Specification {
+    final GradleInternal gradle = Mock()
+    final CacheRepository cacheRepository = Mock()
+    final DefaultTaskArtifactStateCacheAccess cacheAccess = new DefaultTaskArtifactStateCacheAccess(gradle, cacheRepository)
+    
+    def "opens backing cache on first use"() {
+        DirectoryCacheBuilder cacheBuilder = Mock()
+        PersistentCache backingCache = Mock()
+        PersistentIndexedCache<String, Integer> backingIndexedCache = Mock()
+
+        def serializer = new DefaultSerializer<Integer>()
+        when:
+        def indexedCache = cacheAccess.createCache("some-cache", String, Integer, serializer)
+
+        then:
+        0 * _._
+
+        when:
+        indexedCache.get("key")
+
+        then:
+        1 * cacheRepository.cache("taskArtifacts") >> cacheBuilder
+        1 * cacheBuilder.open() >> backingCache
+        _ * cacheBuilder._ >> cacheBuilder
+        _ * backingCache.baseDir >> new File("baseDir")
+        1 * backingCache.createCache(new File("baseDir/some-cache.bin"), String, serializer) >> backingIndexedCache
+        1 * backingIndexedCache.get("key")
+        0 * _._
+    }
+}
diff --git a/subprojects/core/src/test/groovy/org/gradle/api/internal/changedetection/state/FileSnapshotSerializerTest.groovy b/subprojects/core/src/test/groovy/org/gradle/api/internal/changedetection/state/FileSnapshotSerializerTest.groovy
new file mode 100644
index 0000000..7f264d0
--- /dev/null
+++ b/subprojects/core/src/test/groovy/org/gradle/api/internal/changedetection/state/FileSnapshotSerializerTest.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.internal.changedetection.state
+
+import org.gradle.messaging.serialize.SerializerSpec
+
+class FileSnapshotSerializerTest extends SerializerSpec {
+
+    def snapshot = new DefaultFileSnapshotter.FileCollectionSnapshotImpl(["hey": new DefaultFileSnapshotter.DirSnapshot()])
+    def outputSnapshot = new OutputFilesSnapshotter.OutputFilesSnapshot(["foo": 1L, "bar": 2L], snapshot)
+
+    def "handles default snapshots"() {
+        when:
+        DefaultFileSnapshotter.FileCollectionSnapshotImpl out = serialize(snapshot, new FileSnapshotSerializer())
+
+        then:
+        out.snapshots.size() == 1
+        out.snapshots['hey'] instanceof DefaultFileSnapshotter.DirSnapshot
+    }
+
+    def "handles output snapshots"() {
+        when:
+        OutputFilesSnapshotter.OutputFilesSnapshot out = serialize(outputSnapshot, new FileSnapshotSerializer())
+
+        then:
+        out.rootFileIds == ["foo": 1L, "bar": 2L]
+        DefaultFileSnapshotter.FileCollectionSnapshotImpl filesSnapshot = out.filesSnapshot
+        filesSnapshot.snapshots.size() == 1
+        filesSnapshot.snapshots['hey'] instanceof DefaultFileSnapshotter.DirSnapshot
+    }
+}
diff --git a/subprojects/core/src/test/groovy/org/gradle/api/internal/changedetection/state/OutputFilesSnapshotSerializerTest.groovy b/subprojects/core/src/test/groovy/org/gradle/api/internal/changedetection/state/OutputFilesSnapshotSerializerTest.groovy
new file mode 100644
index 0000000..7c4e2db
--- /dev/null
+++ b/subprojects/core/src/test/groovy/org/gradle/api/internal/changedetection/state/OutputFilesSnapshotSerializerTest.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.api.internal.changedetection.state
+
+import org.gradle.messaging.serialize.SerializerSpec
+
+class OutputFilesSnapshotSerializerTest extends SerializerSpec {
+
+    def serializer = new OutputFilesSnapshotSerializer()
+
+    def "reads and writes the snapshot"() {
+        def snapshot = new DefaultFileSnapshotter.FileCollectionSnapshotImpl(["1": new DefaultFileSnapshotter.DirSnapshot()])
+        def outputSnapshot = new OutputFilesSnapshotter.OutputFilesSnapshot(["x": 14L], snapshot)
+
+        when:
+        OutputFilesSnapshotter.OutputFilesSnapshot out = serialize(outputSnapshot, serializer)
+
+        then:
+        ((DefaultFileSnapshotter.FileCollectionSnapshotImpl)out.filesSnapshot).snapshots.size() == 1
+        out.rootFileIds == ['x': 14L]
+    }
+}
diff --git a/subprojects/core/src/test/groovy/org/gradle/api/internal/coerce/TypeCoercingMethodArgumentsTransformerTest.groovy b/subprojects/core/src/test/groovy/org/gradle/api/internal/coerce/TypeCoercingMethodArgumentsTransformerTest.groovy
new file mode 100644
index 0000000..50e39f6
--- /dev/null
+++ b/subprojects/core/src/test/groovy/org/gradle/api/internal/coerce/TypeCoercingMethodArgumentsTransformerTest.groovy
@@ -0,0 +1,67 @@
+/*
+ * Copyright 2013 the original author or authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.gradle.api.internal.coerce
+
+import spock.lang.Specification
+import spock.lang.Unroll
+
+ at SuppressWarnings("GroovyUnusedDeclaration")
+class TypeCoercingMethodArgumentsTransformerTest extends Specification {
+
+    def transformer = new TypeCoercingMethodArgumentsTransformer()
+
+    static enum TestEnum {
+        ABC, DEF
+    }
+
+    static class EnumTester {
+        TestEnum enumProperty
+
+        void oneArgEnumMethod(TestEnum testEnum) {}
+
+        void twoArgEnumMethod(TestEnum testEnum, String other) {}
+
+        void oneArgNonEnumMethod(String other) {}
+    }
+
+    @Unroll
+    def "can enum transform correctly - #desc"() {
+        def i = new EnumTester()
+
+        expect:
+        transformer.transform(i, name, args.toArray()).toList() == transformed
+
+        where:
+        name                  | args    | transformed    | desc
+        "setEnumProperty"     | ["abc"] | [TestEnum.ABC] | "for property"
+        "oneArgEnumMethod"    | ["dEf"] | [TestEnum.DEF] | "one arg method"
+        "twoArgEnumMethod"    | ["abc"] | ["abc"]        | "two arg method"
+        "oneArgNonEnumMethod" | ["abc"] | ["abc"]        | "one arg method non enum method"
+    }
+
+    def "exception thrown when coercing invalid string to enum"() {
+        def i = new EnumTester()
+
+        when:
+        transformer.transform(i, "oneArgEnumMethod", "invalid")
+
+        then:
+        def e = thrown TypeCoercionException
+        e.message.contains TestEnum.values().toString() // error message shows valid values
+    }
+
+}
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 564d959..b0ca056 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
@@ -25,7 +25,7 @@ import org.gradle.api.tasks.TaskDependency;
 import org.gradle.test.fixtures.file.TestFile;
 import org.gradle.test.fixtures.file.TestNameTestDirectoryProvider;
 import org.gradle.util.GUtil;
-import org.gradle.util.HelperUtil;
+import org.gradle.util.TestUtil;
 import org.gradle.util.JUnit4GroovyMockery;
 import org.jmock.Expectations;
 import org.jmock.integration.junit4.JMock;
@@ -275,7 +275,7 @@ public class AbstractFileCollectionTest {
         File file2 = new File("f2");
 
         TestFileCollection collection = new TestFileCollection(file1, file2);
-        FileCollection filtered = collection.filter(HelperUtil.toClosure("{f -> f.name == 'f1'}"));
+        FileCollection filtered = collection.filter(TestUtil.toClosure("{f -> f.name == 'f1'}"));
         assertThat(filtered.getFiles(), equalTo(toSet(file1)));
     }
 
@@ -286,7 +286,7 @@ public class AbstractFileCollectionTest {
         File file3 = new File("dir/f1");
 
         TestFileCollection collection = new TestFileCollection(file1, file2);
-        FileCollection filtered = collection.filter(HelperUtil.toClosure("{f -> f.name == 'f1'}"));
+        FileCollection filtered = collection.filter(TestUtil.toClosure("{f -> f.name == 'f1'}"));
         assertThat(filtered.getFiles(), equalTo(toSet(file1)));
 
         collection.files.add(file3);
@@ -304,14 +304,14 @@ public class AbstractFileCollectionTest {
         collection.files.add(new File("f1"));
 
         assertHasSameDependencies(collection.getAsFileTree());
-        assertHasSameDependencies(collection.getAsFileTree().matching(HelperUtil.TEST_CLOSURE));
+        assertHasSameDependencies(collection.getAsFileTree().matching(TestUtil.TEST_CLOSURE));
     }
 
     @Test
     public void filteredCollectionHasSameDependenciesAsThis() {
         TestFileCollectionWithDependency collection = new TestFileCollectionWithDependency();
 
-        assertHasSameDependencies(collection.filter(HelperUtil.toClosure("{true}")));
+        assertHasSameDependencies(collection.filter(TestUtil.toClosure("{true}")));
     }
 
     private void assertHasSameDependencies(FileCollection tree) {
diff --git a/subprojects/core/src/test/groovy/org/gradle/api/internal/file/BaseDirFileResolverTest.groovy b/subprojects/core/src/test/groovy/org/gradle/api/internal/file/BaseDirFileResolverTest.groovy
index 10115b1..aa838ce 100644
--- a/subprojects/core/src/test/groovy/org/gradle/api/internal/file/BaseDirFileResolverTest.groovy
+++ b/subprojects/core/src/test/groovy/org/gradle/api/internal/file/BaseDirFileResolverTest.groovy
@@ -33,9 +33,6 @@ import java.util.concurrent.Callable
 import static org.hamcrest.Matchers.*
 import static org.junit.Assert.*
 
-/**
- * @author Hans Dockter
- */
 class BaseDirFileResolverTest {
     static final String TEST_PATH = 'testpath'
 
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 e0382ca..1ce86c3 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
@@ -21,7 +21,7 @@ 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.TestUtil;
 import org.gradle.util.JUnit4GroovyMockery;
 import org.jmock.Expectations;
 import org.jmock.integration.junit4.JMock;
@@ -266,12 +266,12 @@ public class CompositeFileCollectionTest {
     @Test
     public void fileTreeDependsOnUnionOfAllDependencies() {
         assertDependsOnUnionOfSourceCollections(collection.getAsFileTree());
-        assertDependsOnUnionOfSourceCollections(collection.getAsFileTree().matching(HelperUtil.TEST_CLOSURE));
+        assertDependsOnUnionOfSourceCollections(collection.getAsFileTree().matching(TestUtil.TEST_CLOSURE));
     }
 
     @Test
     public void filteredCollectionDependsOnUnionOfAllDependencies() {
-        assertDependsOnUnionOfSourceCollections(collection.filter(HelperUtil.TEST_CLOSURE));
+        assertDependsOnUnionOfSourceCollections(collection.filter(TestUtil.TEST_CLOSURE));
     }
 
     private void assertDependsOnUnionOfSourceCollections(FileCollection collection) {
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 5bf5d8c..057e82b 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
@@ -20,7 +20,7 @@ import org.gradle.api.file.FileTree;
 import org.gradle.api.file.FileVisitor;
 import org.gradle.api.internal.file.collections.FileCollectionResolveContext;
 import org.gradle.api.tasks.util.PatternSet;
-import org.gradle.util.HelperUtil;
+import org.gradle.util.TestUtil;
 import static org.gradle.util.WrapUtil.*;
 import static org.hamcrest.Matchers.*;
 
@@ -52,7 +52,7 @@ public class CompositeFileTreeTest {
 
     @Test
     public void matchingWithClosureReturnsUnionOfFilteredSets() {
-        final Closure closure = HelperUtil.TEST_CLOSURE;
+        final Closure closure = TestUtil.TEST_CLOSURE;
         final FileTree filtered1 = context.mock(FileTree.class);
         final FileTree filtered2 = context.mock(FileTree.class);
 
@@ -114,7 +114,7 @@ public class CompositeFileTreeTest {
 
     @Test
     public void visitsEachTreeWithClosure() {
-        final Closure visitor = HelperUtil.TEST_CLOSURE;
+        final Closure visitor = TestUtil.TEST_CLOSURE;
 
         context.checking(new Expectations() {{
             one(source1).visit(visitor);
diff --git a/subprojects/core/src/test/groovy/org/gradle/api/internal/file/DefaultCompositeFileTreeTest.groovy b/subprojects/core/src/test/groovy/org/gradle/api/internal/file/DefaultCompositeFileTreeTest.groovy
new file mode 100644
index 0000000..bb8175c
--- /dev/null
+++ b/subprojects/core/src/test/groovy/org/gradle/api/internal/file/DefaultCompositeFileTreeTest.groovy
@@ -0,0 +1,68 @@
+/*
+ * Copyright 2013 the original author or authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY 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.internal.nativeplatform.filesystem.FileSystems
+import org.gradle.test.fixtures.file.WorkspaceTest
+
+class DefaultCompositeFileTreeTest extends WorkspaceTest {
+
+    def "can be empty"() {
+        when:
+        def ft = new DefaultCompositeFileTree(Collections.emptyList())
+
+        then:
+        ft.files.isEmpty()
+    }
+
+    def "contains all files"() {
+        given:
+        def a1 = file("a/1.txt") << "a/1"
+        def b1 = file("b/1.txt") << "b/1"
+        def fileResolver = new BaseDirFileResolver(FileSystems.getDefault(), testDirectory)
+
+        when:
+        def a = fileResolver.resolveFilesAsTree("a")
+        def b = fileResolver.resolveFilesAsTree("b")
+        def composite = new DefaultCompositeFileTree(Arrays.asList(a, b))
+
+        then:
+        composite.files == [a1, b1].toSet()
+    }
+
+    def "can visit all files"() {
+        given:
+        def a1 = file("a/1.txt") << "a/1"
+        def b1 = file("b/1.txt") << "b/1"
+        def fileResolver = new BaseDirFileResolver(FileSystems.getDefault(), testDirectory)
+
+        when:
+        def a = fileResolver.resolveFilesAsTree("a")
+        def b = fileResolver.resolveFilesAsTree("b")
+        def composite = new DefaultCompositeFileTree(Arrays.asList(a, b))
+
+        and:
+        def visited = []
+        composite.visit {
+            visited << it.file
+        }
+
+        then:
+        visited.toSet() == [a1, b1].toSet()
+    }
+
+}
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 43c5019..2acba65 100755
--- 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
@@ -26,15 +26,16 @@ 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.file.copy.DefaultCopySpec
 import org.gradle.api.internal.tasks.TaskResolver
 import org.gradle.internal.os.OperatingSystem
+import org.gradle.internal.reflect.DirectInstantiator
+import org.gradle.internal.reflect.Instantiator
 import org.gradle.process.ExecResult
 import org.gradle.process.internal.ExecException
 import org.gradle.test.fixtures.file.TestFile
 import org.gradle.test.fixtures.file.TestNameTestDirectoryProvider
-import org.gradle.util.ClasspathUtil
+import org.gradle.internal.classloader.ClasspathUtil
 import org.junit.Rule
 import org.junit.Test
 import spock.lang.Specification
@@ -43,7 +44,8 @@ public class DefaultFileOperationsTest extends Specification {
     private final FileResolver resolver = Mock()
     private final TaskResolver taskResolver = Mock()
     private final TemporaryFileProvider temporaryFileProvider = Mock()
-    private DefaultFileOperations fileOperations = new DefaultFileOperations(resolver, taskResolver, temporaryFileProvider)
+    private final Instantiator instantiator = new DirectInstantiator()
+    private DefaultFileOperations fileOperations = new DefaultFileOperations(resolver, taskResolver, temporaryFileProvider, instantiator)
     @Rule
     public final TestNameTestDirectoryProvider tmpDir = new TestNameTestDirectoryProvider()
 
@@ -171,7 +173,6 @@ public class DefaultFileOperationsTest extends Specification {
         def result = fileOperations.copy { from 'file'; into 'dir' }
 
         then:
-        result instanceof CopyActionImpl
         !result.didWork
     }
 
@@ -216,7 +217,7 @@ public class DefaultFileOperationsTest extends Specification {
         def spec = fileOperations.copySpec { include 'pattern'}
 
         then:
-        spec instanceof CopySpecImpl
+        spec instanceof DefaultCopySpec
         spec.includes == ['pattern'] as Set
     }
 
@@ -240,7 +241,7 @@ public class DefaultFileOperationsTest extends Specification {
 
     def javaexec() {
         File testFile = tmpDir.file("someFile")
-        fileOperations = new DefaultFileOperations(resolver(), taskResolver, temporaryFileProvider)
+        fileOperations = new DefaultFileOperations(resolver(), taskResolver, temporaryFileProvider, instantiator)
         List files = ClasspathUtil.getClasspath(getClass().classLoader)
 
         when:
@@ -256,7 +257,7 @@ public class DefaultFileOperationsTest extends Specification {
     }
 
     def javaexecWithNonZeroExitValueShouldThrowException() {
-        fileOperations = new DefaultFileOperations(resolver(), taskResolver, temporaryFileProvider)
+        fileOperations = new DefaultFileOperations(resolver(), taskResolver, temporaryFileProvider, instantiator)
 
         when:
         fileOperations.javaexec {
@@ -268,7 +269,7 @@ public class DefaultFileOperationsTest extends Specification {
     }
 
     def javaexecWithNonZeroExitValueAndIgnoreExitValueShouldNotThrowException() {
-        fileOperations = new DefaultFileOperations(resolver(), taskResolver, temporaryFileProvider)
+        fileOperations = new DefaultFileOperations(resolver(), taskResolver, temporaryFileProvider, instantiator)
 
         when:
         ExecResult result = fileOperations.javaexec {
@@ -285,7 +286,7 @@ public class DefaultFileOperationsTest extends Specification {
             return
         }
 
-        fileOperations = new DefaultFileOperations(resolver(), taskResolver, temporaryFileProvider)
+        fileOperations = new DefaultFileOperations(resolver(), taskResolver, temporaryFileProvider, instantiator)
         File testFile = tmpDir.file("someFile")
 
         when:
@@ -304,7 +305,7 @@ public class DefaultFileOperationsTest extends Specification {
         if (OperatingSystem.current().isWindows()) {
             return
         }
-        fileOperations = new DefaultFileOperations(resolver(), taskResolver, temporaryFileProvider)
+        fileOperations = new DefaultFileOperations(resolver(), taskResolver, temporaryFileProvider, instantiator)
 
         when:
         fileOperations.exec {
@@ -321,7 +322,7 @@ public class DefaultFileOperationsTest extends Specification {
         if (OperatingSystem.current().isWindows()) {
             return
         }
-        fileOperations = new DefaultFileOperations(resolver(), taskResolver, temporaryFileProvider)
+        fileOperations = new DefaultFileOperations(resolver(), taskResolver, temporaryFileProvider, instantiator)
 
         when:
         ExecResult result = fileOperations.exec {
diff --git a/subprojects/core/src/test/groovy/org/gradle/api/internal/file/DelegatingFileCollectionTest.groovy b/subprojects/core/src/test/groovy/org/gradle/api/internal/file/DelegatingFileCollectionTest.groovy
new file mode 100644
index 0000000..073e9fe
--- /dev/null
+++ b/subprojects/core/src/test/groovy/org/gradle/api/internal/file/DelegatingFileCollectionTest.groovy
@@ -0,0 +1,104 @@
+/*
+ * Copyright 2013 the original author or authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY 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.FileCollection
+import org.gradle.api.internal.file.collections.DelegatingFileCollection
+import org.gradle.api.internal.file.collections.MinimalFileSet
+import org.gradle.api.specs.Spec
+import org.gradle.api.specs.Specs
+
+import spock.lang.Specification
+
+class DelegatingFileCollectionTest extends Specification {
+    FileCollection delegatedTo = Mock()
+    DelegatingFileCollection fileCollection = new DelegatingFileCollection() {
+        @Override
+        FileCollection getDelegate() {
+            delegatedTo
+        }
+    }
+
+    File aFile = new File("foo")
+    FileCollection aCollection = Stub()
+    Object anObject = new Object()
+
+    def "delegates all method calls"() {
+        when:
+        fileCollection.with {
+            getSingleFile()
+            getFiles()
+            contains(aFile)
+            getAsPath()
+            plus(aCollection)
+            minus(aCollection)
+            filter({ true })
+            filter(Specs.satisfyAll())
+            delegate.asType(List) // avoid collision with DGM method
+            add(aCollection)
+            isEmpty()
+            stopExecutionIfEmpty()
+            getAsFileTree()
+            addToAntBuilder(anObject, "nodeName", FileCollection.AntType.MatchingTask)
+            addToAntBuilder(anObject, "nodeName")
+            getBuildDependencies()
+            delegate.iterator() // avoid collision with DGM method
+        }
+
+        then:
+        with(delegatedTo) {
+            1 * getSingleFile()
+            1 * getFiles()
+            1 * contains({ it.is(aFile) })
+            1 * getAsPath()
+            1 * plus({ it.is(aCollection) })
+            1 * minus({ it.is(aCollection) })
+            1 * filter(_ as Closure)
+            1 * filter(_ as Spec)
+            1 * asType(List)
+            1 * add({ it.is(aCollection) })
+            1 * isEmpty()
+            1 * stopExecutionIfEmpty()
+            1 * getAsFileTree()
+            1 * addToAntBuilder(anObject, "nodeName", FileCollection.AntType.MatchingTask)
+            1 * addToAntBuilder(anObject, "nodeName")
+            1 * getBuildDependencies()
+            1 * iterator()
+            0 * _
+        }
+    }
+
+    interface MyFileCollection extends FileCollection, MinimalFileSet {}
+
+    def "delegates getDisplayName() to toString() if delegate is not a MinimalFileSet"() {
+        when:
+        fileCollection.getDisplayName()
+
+        then:
+        1 * delegatedTo.toString()
+
+    }
+
+    def "delegates getDisplayName() to getDisplayName() if delegate is a MinimalFileSet"() {
+        delegatedTo = Mock(MyFileCollection)
+
+        when:
+        fileCollection.getDisplayName()
+
+        then:
+        1 * delegatedTo.getDisplayName()
+    }
+}
diff --git a/subprojects/core/src/test/groovy/org/gradle/api/internal/file/LazilyInitializedFileCollectionTest.groovy b/subprojects/core/src/test/groovy/org/gradle/api/internal/file/LazilyInitializedFileCollectionTest.groovy
new file mode 100644
index 0000000..c5734a2
--- /dev/null
+++ b/subprojects/core/src/test/groovy/org/gradle/api/internal/file/LazilyInitializedFileCollectionTest.groovy
@@ -0,0 +1,52 @@
+/*
+ * Copyright 2013 the original author or authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY 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.FileCollection
+import org.gradle.api.internal.file.collections.LazilyInitializedFileCollection
+import org.gradle.api.internal.file.collections.SimpleFileCollection
+
+import spock.lang.Specification
+
+class LazilyInitializedFileCollectionTest extends Specification {
+    def createCount = 0
+    def fileCollection = new LazilyInitializedFileCollection() {
+        @Override
+        FileCollection createDelegate() {
+            createCount++
+            new SimpleFileCollection([new File("foo")])
+        }
+    }
+
+    def "creates delegate on first access"() {
+        expect:
+        createCount == 0
+
+        when:
+        def files = fileCollection.files
+
+        then:
+        createCount == 1
+        files == [new File("foo")] as Set
+
+        when:
+        fileCollection.files
+
+        then:
+        createCount == 1
+        files == [new File("foo")] as Set
+    }
+}
diff --git a/subprojects/core/src/test/groovy/org/gradle/api/internal/file/MaybeCompressedFileResourceTest.groovy b/subprojects/core/src/test/groovy/org/gradle/api/internal/file/MaybeCompressedFileResourceTest.groovy
index c07f889..d3a886b 100644
--- a/subprojects/core/src/test/groovy/org/gradle/api/internal/file/MaybeCompressedFileResourceTest.groovy
+++ b/subprojects/core/src/test/groovy/org/gradle/api/internal/file/MaybeCompressedFileResourceTest.groovy
@@ -21,9 +21,6 @@ import org.gradle.api.internal.file.archive.compression.Bzip2Archiver
 import org.gradle.api.internal.file.archive.compression.GzipArchiver
 import spock.lang.Specification
 
-/**
- * by Szczepan Faber, created at: 11/24/11
- */
 public class MaybeCompressedFileResourceTest extends Specification {
 
     def "understands file extensions"() {
diff --git a/subprojects/core/src/test/groovy/org/gradle/api/internal/file/archive/TarCopyActionTest.java b/subprojects/core/src/test/groovy/org/gradle/api/internal/file/archive/TarCopyActionTest.java
new file mode 100644
index 0000000..bdcead0
--- /dev/null
+++ b/subprojects/core/src/test/groovy/org/gradle/api/internal/file/archive/TarCopyActionTest.java
@@ -0,0 +1,244 @@
+/*
+ * Copyright 2011 the original author or authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY 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.archive;
+
+import org.apache.commons.io.IOUtils;
+import org.gradle.api.GradleException;
+import org.gradle.api.file.RelativePath;
+import org.gradle.api.internal.file.CopyActionProcessingStreamAction;
+import org.gradle.api.internal.file.FileResource;
+import org.gradle.api.internal.file.archive.compression.ArchiveOutputStreamFactory;
+import org.gradle.api.internal.file.archive.compression.Bzip2Archiver;
+import org.gradle.api.internal.file.archive.compression.GzipArchiver;
+import org.gradle.api.internal.file.archive.compression.SimpleCompressor;
+import org.gradle.api.internal.file.copy.CopyActionProcessingStream;
+import org.gradle.api.internal.file.copy.FileCopyDetailsInternal;
+import org.gradle.test.fixtures.file.TestFile;
+import org.gradle.test.fixtures.file.TestNameTestDirectoryProvider;
+import org.hamcrest.Description;
+import org.jmock.Expectations;
+import org.jmock.api.Invocation;
+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.OutputStream;
+import java.util.HashMap;
+import java.util.Map;
+
+import static org.gradle.api.file.FileVisitorUtil.assertVisitsPermissions;
+import static org.gradle.api.internal.file.copy.CopyActionExecuterUtil.visit;
+import static org.hamcrest.Matchers.*;
+import static org.junit.Assert.assertThat;
+import static org.junit.Assert.fail;
+
+ at RunWith(JMock.class)
+public class TarCopyActionTest {
+    @Rule
+    public final TestNameTestDirectoryProvider tmpDir = new TestNameTestDirectoryProvider();
+    private final JUnit4Mockery context = new JUnit4Mockery();
+    private TarCopyAction action;
+
+
+    @Test
+    public void createsTarFile() {
+        final TestFile tarFile = initializeTarFile(tmpDir.getTestDirectory().file("test.tar"),
+                new SimpleCompressor());
+        tarAndUntarAndCheckFileContents(tarFile);
+    }
+
+    private void tarAndUntarAndCheckFileContents(TestFile tarFile) {
+        tar(file("dir/file1"), file("file2"));
+
+        TestFile expandDir = tmpDir.getTestDirectory().file("expanded");
+        tarFile.untarTo(expandDir);
+        expandDir.file("dir/file1").assertContents(equalTo("contents of dir/file1"));
+        expandDir.file("file2").assertContents(equalTo("contents of file2"));
+    }
+
+    @Test
+    public void createsGzipCompressedTarFile() {
+        final TestFile tarFile = initializeTarFile(tmpDir.getTestDirectory().file("test.tgz"),
+                GzipArchiver.getCompressor());
+        tarAndUntarAndCheckFileContents(tarFile);
+    }
+
+    @Test
+    public void createsBzip2CompressedTarFile() {
+        final TestFile tarFile = initializeTarFile(tmpDir.getTestDirectory().file("test.tbz2"),
+                Bzip2Archiver.getCompressor());
+        tarAndUntarAndCheckFileContents(tarFile);
+    }
+
+    @Test
+    public void tarFileContainsExpectedPermissions() {
+        final TestFile tarFile = initializeTarFile(tmpDir.getTestDirectory().file("test.tar"),
+                new SimpleCompressor());
+
+        tar(dir("dir"), file("file"));
+
+        Map<String, Integer> expected = new HashMap<String, Integer>();
+        expected.put("dir", 2);
+        expected.put("file", 1);
+
+        assertVisitsPermissions(new TarFileTree(new FileResource(tarFile), null),
+                expected);
+    }
+
+    @Test
+    public void wrapsFailureToOpenOutputFile() {
+        final TestFile tarFile = initializeTarFile(tmpDir.createDir("test.tar"),
+                new SimpleCompressor());
+
+        try {
+            action.execute(new CopyActionProcessingStream() {
+                public void process(CopyActionProcessingStreamAction action) {
+                    // nothing
+                }
+            });
+            fail();
+        } catch (GradleException e) {
+            assertThat(e.getMessage(), equalTo(String.format("Could not create TAR '%s'.", tarFile)));
+        }
+    }
+
+    @Test
+    public void wrapsFailureToAddElement() {
+        final TestFile tarFile = initializeTarFile(tmpDir.getTestDirectory().file("test.tar"),
+                new SimpleCompressor());
+
+        Throwable failure = new RuntimeException("broken");
+        try {
+            visit(action, brokenFile("dir/file1", failure));
+            fail();
+        } catch (GradleException e) {
+            assertThat(e.getMessage(), equalTo(String.format("Could not add [dir/file1] to TAR '%s'.", tarFile)));
+            assertThat(e.getCause(), sameInstance(failure));
+        }
+    }
+
+    private TestFile initializeTarFile(final TestFile tarFile, final ArchiveOutputStreamFactory compressor) {
+        action = new TarCopyAction(tarFile, compressor);
+        return tarFile;
+    }
+
+    private void tar(final FileCopyDetailsInternal... files) {
+        action.execute(new CopyActionProcessingStream() {
+            public void process(CopyActionProcessingStreamAction action) {
+                for (FileCopyDetailsInternal f : files) {
+                    if (f.isDirectory()) {
+                        action.processFile(f);
+                    } else {
+                        action.processFile(f);
+                    }
+                }
+            }
+        });
+    }
+
+    private FileCopyDetailsInternal file(final String path) {
+        final FileCopyDetailsInternal details = context.mock(FileCopyDetailsInternal.class, path);
+        final String content = String.format("contents of %s", path);
+
+        context.checking(new Expectations() {{
+            allowing(details).getRelativePath();
+            will(returnValue(RelativePath.parse(true, path)));
+
+            allowing(details).getLastModified();
+            will(returnValue(1000L));
+
+            allowing(details).getSize();
+            will(returnValue((long) content.getBytes().length));
+
+            allowing(details).isDirectory();
+            will(returnValue(false));
+
+            allowing(details).getMode();
+            will(returnValue(1));
+
+            allowing(details).copyTo(with(notNullValue(OutputStream.class)));
+            will(new org.jmock.api.Action() {
+                public void describeTo(Description description) {
+                    description.appendText("write content");
+                }
+
+                public Object invoke(Invocation invocation) throws Throwable {
+                    IOUtils.write(content, (OutputStream) invocation.getParameter(0));
+                    return null;
+                }
+            });
+        }});
+
+        return details;
+    }
+
+    private FileCopyDetailsInternal dir(final String path) {
+        final FileCopyDetailsInternal details = context.mock(FileCopyDetailsInternal.class, path);
+
+        context.checking(new Expectations() {{
+            allowing(details).getRelativePath();
+            will(returnValue(RelativePath.parse(false, path)));
+
+            allowing(details).getLastModified();
+            will(returnValue(1000L));
+
+            allowing(details).isDirectory();
+            will(returnValue(true));
+
+            allowing(details).getMode();
+            will(returnValue(2));
+        }});
+
+        return details;
+    }
+
+    private FileCopyDetailsInternal brokenFile(final String path, final Throwable failure) {
+        final FileCopyDetailsInternal details = context.mock(FileCopyDetailsInternal.class, String.format("[%s]", path));
+
+        context.checking(new Expectations() {{
+            allowing(details).getRelativePath();
+            will(returnValue(RelativePath.parse(true, path)));
+
+            allowing(details).getLastModified();
+            will(returnValue(1000L));
+
+            allowing(details).getSize();
+            will(returnValue(1000L));
+
+            allowing(details).isDirectory();
+            will(returnValue(false));
+
+            allowing(details).getMode();
+            will(returnValue(1));
+
+            allowing(details).copyTo(with(notNullValue(OutputStream.class)));
+            will(new org.jmock.api.Action() {
+                public void describeTo(Description description) {
+                    description.appendText("write content");
+                }
+
+                public Object invoke(Invocation invocation) throws Throwable {
+                    failure.fillInStackTrace();
+                    throw failure;
+                }
+            });
+        }});
+
+        return details;
+    }
+}
diff --git a/subprojects/core/src/test/groovy/org/gradle/api/internal/file/archive/TarCopySpecVisitorTest.java b/subprojects/core/src/test/groovy/org/gradle/api/internal/file/archive/TarCopySpecVisitorTest.java
deleted file mode 100644
index 6f4d75f..0000000
--- a/subprojects/core/src/test/groovy/org/gradle/api/internal/file/archive/TarCopySpecVisitorTest.java
+++ /dev/null
@@ -1,250 +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.api.internal.file.archive;
-
-import org.apache.commons.io.IOUtils;
-import org.gradle.api.GradleException;
-import org.gradle.api.file.FileVisitDetails;
-import org.gradle.api.file.RelativePath;
-import org.gradle.api.internal.file.FileResource;
-import org.gradle.api.internal.file.archive.compression.ArchiveOutputStreamFactory;
-import org.gradle.api.internal.file.archive.compression.Bzip2Archiver;
-import org.gradle.api.internal.file.archive.compression.GzipArchiver;
-import org.gradle.api.internal.file.archive.compression.SimpleCompressor;
-import org.gradle.api.internal.file.copy.ArchiveCopyAction;
-import org.gradle.api.internal.file.copy.ReadableCopySpec;
-import org.gradle.test.fixtures.file.TestFile;
-import org.gradle.test.fixtures.file.TestNameTestDirectoryProvider;
-import org.hamcrest.Description;
-import org.jmock.Expectations;
-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.Rule;
-import org.junit.Test;
-import org.junit.runner.RunWith;
-
-import java.io.OutputStream;
-import java.util.HashMap;
-import java.util.Map;
-
-import static org.gradle.api.file.FileVisitorUtil.assertVisitsPermissions;
-import static org.hamcrest.Matchers.*;
-import static org.junit.Assert.assertThat;
-import static org.junit.Assert.fail;
-
- at RunWith(JMock.class)
-public class TarCopySpecVisitorTest {
-    @Rule
-    public final TestNameTestDirectoryProvider tmpDir = new TestNameTestDirectoryProvider();
-    private final JUnit4Mockery context = new JUnit4Mockery();
-    private final ArchiveCopyAction copyAction = context.mock(ArchiveCopyAction.class);
-    private final ReadableCopySpec copySpec = context.mock(ReadableCopySpec.class);
-    private final TarCopySpecVisitor visitor = new TarCopySpecVisitor();
-
-    @Test
-    public void createsTarFile() {
-        final TestFile tarFile = initializeTarFile(tmpDir.getTestDirectory().file("test.tar"),
-            new SimpleCompressor());
-        tarAndUntarAndCheckFileContents(tarFile);
-    }
-
-    private void tarAndUntarAndCheckFileContents(TestFile tarFile) {
-        tar(file("dir/file1"), file("file2"));
-
-        TestFile expandDir = tmpDir.getTestDirectory().file("expanded");
-        tarFile.untarTo(expandDir);
-        expandDir.file("dir/file1").assertContents(equalTo("contents of dir/file1"));
-        expandDir.file("file2").assertContents(equalTo("contents of file2"));
-    }
-
-    @Test
-    public void createsGzipCompressedTarFile() {
-        final TestFile tarFile = initializeTarFile(tmpDir.getTestDirectory().file("test.tgz"),
-            GzipArchiver.getCompressor());
-        tarAndUntarAndCheckFileContents(tarFile);
-    }
-
-    @Test
-    public void createsBzip2CompressedTarFile() {
-        final TestFile tarFile = initializeTarFile(tmpDir.getTestDirectory().file("test.tbz2"),
-            Bzip2Archiver.getCompressor());
-        tarAndUntarAndCheckFileContents(tarFile);
-    }
-
-    @Test
-    public void tarFileContainsExpectedPermissions() {
-        final TestFile tarFile = initializeTarFile(tmpDir.getTestDirectory().file("test.tar"),
-            new SimpleCompressor());
-
-        tar(dir("dir"), file("file"));
-
-        Map<String, Integer> expected = new HashMap<String, Integer>();
-        expected.put("dir", 2);
-        expected.put("file", 1);
-
-        assertVisitsPermissions(new TarFileTree(new FileResource(tarFile), null),
-            expected);
-    }
-
-    @Test
-    public void wrapsFailureToOpenOutputFile() {
-        final TestFile tarFile = initializeTarFile(tmpDir.createDir("test.tar"),
-            new SimpleCompressor());
-
-        try {
-            visitor.startVisit(copyAction);
-            fail();
-        } catch (GradleException e) {
-            assertThat(e.getMessage(), equalTo(String.format("Could not create TAR '%s'.", tarFile)));
-        }
-    }
-
-    @Test
-    public void wrapsFailureToAddElement() {
-        final TestFile tarFile = initializeTarFile(tmpDir.getTestDirectory().file("test.tar"),
-            new SimpleCompressor());
-
-        visitor.startVisit(copyAction);
-        visitor.visitSpec(copySpec);
-
-        Throwable failure = new RuntimeException("broken");
-        try {
-            visitor.visitFile(brokenFile("dir/file1", failure));
-            fail();
-        } catch (GradleException e) {
-            assertThat(e.getMessage(), equalTo(String.format("Could not add [dir/file1] to TAR '%s'.", tarFile)));
-            assertThat(e.getCause(), sameInstance(failure));
-        }
-    }
-
-    private TestFile initializeTarFile(final TestFile tarFile, final ArchiveOutputStreamFactory compressor) {
-        context.checking(new Expectations() {{
-            allowing(copyAction).getArchivePath();
-            will(returnValue(tarFile));
-            allowing(copyAction).getCompressor();
-            will(returnValue(compressor));
-        }});
-        return tarFile;
-    }
-
-    private void tar(FileVisitDetails... files) {
-        visitor.startVisit(copyAction);
-        visitor.visitSpec(copySpec);
-
-        for (FileVisitDetails f : files) {
-            if (f.isDirectory()) {
-                visitor.visitDir(f);
-            } else {
-                visitor.visitFile(f);
-            }
-        }
-
-        visitor.endVisit();
-    }
-
-    private FileVisitDetails file(final String path) {
-        final FileVisitDetails details = context.mock(FileVisitDetails.class, path);
-        final String content = String.format("contents of %s", path);
-
-        context.checking(new Expectations() {{
-            allowing(details).getRelativePath();
-            will(returnValue(RelativePath.parse(true, path)));
-
-            allowing(details).getLastModified();
-            will(returnValue(1000L));
-
-            allowing(details).getSize();
-            will(returnValue((long)content.getBytes().length));
-
-            allowing(details).isDirectory();
-            will(returnValue(false));
-
-            allowing(details).getMode();
-            will(returnValue(1));
-
-            allowing(details).copyTo(with(notNullValue(OutputStream.class)));
-            will(new Action() {
-                public void describeTo(Description description) {
-                    description.appendText("write content");
-                }
-
-                public Object invoke(Invocation invocation) throws Throwable {
-                    IOUtils.write(content, (OutputStream) invocation.getParameter(0));
-                    return null;
-                }
-            });
-        }});
-
-        return details;
-    }
-
-    private FileVisitDetails dir(final String path) {
-        final FileVisitDetails details = context.mock(FileVisitDetails.class, path);
-
-        context.checking(new Expectations() {{
-            allowing(details).getRelativePath();
-            will(returnValue(RelativePath.parse(false, path)));
-
-            allowing(details).getLastModified();
-            will(returnValue(1000L));
-
-            allowing(details).isDirectory();
-            will(returnValue(true));
-
-            allowing(details).getMode();
-            will(returnValue(2));
-        }});
-
-        return details;
-    }
-
-    private FileVisitDetails brokenFile(final String path, final Throwable failure) {
-        final FileVisitDetails details = context.mock(FileVisitDetails.class, String.format("[%s]", path));
-
-        context.checking(new Expectations() {{
-            allowing(details).getRelativePath();
-            will(returnValue(RelativePath.parse(true, path)));
-
-            allowing(details).getLastModified();
-            will(returnValue(1000L));
-
-            allowing(details).getSize();
-            will(returnValue(1000L));
-
-            allowing(details).isDirectory();
-            will(returnValue(false));
-
-            allowing(details).getMode();
-            will(returnValue(1));
-
-            allowing(details).copyTo(with(notNullValue(OutputStream.class)));
-            will(new Action() {
-                public void describeTo(Description description) {
-                    description.appendText("write content");
-                }
-
-                public Object invoke(Invocation invocation) throws Throwable {
-                    failure.fillInStackTrace();
-                    throw failure;
-                }
-            });
-        }});
-
-        return details;
-    }
-}
diff --git a/subprojects/core/src/test/groovy/org/gradle/api/internal/file/archive/ZipCopyActionTest.java b/subprojects/core/src/test/groovy/org/gradle/api/internal/file/archive/ZipCopyActionTest.java
new file mode 100644
index 0000000..cbfaefa
--- /dev/null
+++ b/subprojects/core/src/test/groovy/org/gradle/api/internal/file/archive/ZipCopyActionTest.java
@@ -0,0 +1,215 @@
+/*
+ * Copyright 2010 the original author or authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY 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.archive;
+
+import org.apache.commons.io.IOUtils;
+import org.gradle.api.GradleException;
+import org.gradle.api.file.RelativePath;
+import org.gradle.api.internal.file.CopyActionProcessingStreamAction;
+import org.gradle.api.internal.file.copy.CopyActionProcessingStream;
+import org.gradle.api.internal.file.copy.FileCopyDetailsInternal;
+import org.gradle.api.internal.file.copy.ZipStoredCompressor;
+import org.gradle.test.fixtures.file.TestFile;
+import org.gradle.test.fixtures.file.TestNameTestDirectoryProvider;
+import org.hamcrest.Description;
+import org.jmock.Expectations;
+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.OutputStream;
+import java.util.HashMap;
+import java.util.Map;
+
+import static org.gradle.api.file.FileVisitorUtil.assertVisitsPermissions;
+import static org.gradle.api.internal.file.copy.CopyActionExecuterUtil.visit;
+import static org.hamcrest.Matchers.*;
+import static org.junit.Assert.assertThat;
+import static org.junit.Assert.fail;
+
+ at RunWith(JMock.class)
+public class ZipCopyActionTest {
+    @Rule
+    public final TestNameTestDirectoryProvider tmpDir = new TestNameTestDirectoryProvider();
+    private final JUnit4Mockery context = new JUnit4Mockery();
+    private ZipCopyAction visitor;
+    private TestFile zipFile;
+
+    @Before
+    public void setup() {
+        zipFile = tmpDir.getTestDirectory().file("test.zip");
+        visitor = new ZipCopyAction(zipFile, ZipStoredCompressor.INSTANCE);
+    }
+
+    @Test
+    public void createsZipFile() {
+        zip(dir("dir"), file("dir/file1"), file("file2"));
+
+        TestFile expandDir = tmpDir.getTestDirectory().file("expanded");
+        zipFile.unzipTo(expandDir);
+        expandDir.file("dir/file1").assertContents(equalTo("contents of dir/file1"));
+        expandDir.file("file2").assertContents(equalTo("contents of file2"));
+    }
+
+    @Test
+    public void createsDeflatedZipFile() {
+        zip(dir("dir"), file("dir/file1"), file("file2"));
+
+        TestFile expandDir = tmpDir.getTestDirectory().file("expanded");
+        zipFile.unzipTo(expandDir);
+        expandDir.file("dir/file1").assertContents(equalTo("contents of dir/file1"));
+        expandDir.file("file2").assertContents(equalTo("contents of file2"));
+    }
+
+    @Test
+    public void zipFileContainsExpectedPermissions() {
+        zip(dir("dir"), file("file"));
+
+        Map<String, Integer> expected = new HashMap<String, Integer>();
+        expected.put("dir", 2);
+        expected.put("file", 1);
+
+        assertVisitsPermissions(new ZipFileTree(zipFile, null), expected);
+    }
+
+    @Test
+    public void wrapsFailureToOpenOutputFile() {
+        final TestFile invalidZipFile = tmpDir.createDir("test.zip");
+        visitor = new ZipCopyAction(invalidZipFile, ZipStoredCompressor.INSTANCE);
+
+        try {
+            visitor.execute(new CopyActionProcessingStream() {
+                public void process(CopyActionProcessingStreamAction action) {
+                    // nothing
+                }
+            });
+            fail();
+        } catch (GradleException e) {
+            assertThat(e.getMessage(), equalTo(String.format("Could not create ZIP '%s'.", zipFile)));
+        }
+    }
+
+    @Test
+    public void wrapsFailureToAddElement() {
+
+        Throwable failure = new RuntimeException("broken");
+        try {
+            visit(visitor, brokenFile("dir/file1", failure));
+            fail();
+        } catch (GradleException e) {
+            assertThat(e.getMessage(), equalTo(String.format("Could not add [dir/file1] to ZIP '%s'.", zipFile)));
+            assertThat(e.getCause(), sameInstance(failure));
+        }
+    }
+
+    private void zip(final FileCopyDetailsInternal... files) {
+        visitor.execute(new CopyActionProcessingStream() {
+            public void process(CopyActionProcessingStreamAction action) {
+                for (FileCopyDetailsInternal f : files) {
+                    action.processFile(f);
+                }
+            }
+        });
+    }
+
+    private FileCopyDetailsInternal file(final String path) {
+        final FileCopyDetailsInternal details = context.mock(FileCopyDetailsInternal.class, path);
+
+        context.checking(new Expectations() {{
+            allowing(details).getRelativePath();
+            will(returnValue(RelativePath.parse(true, path)));
+
+            allowing(details).getLastModified();
+            will(returnValue(1000L));
+
+            allowing(details).isDirectory();
+            will(returnValue(false));
+
+            allowing(details).getMode();
+            will(returnValue(1));
+
+            allowing(details).copyTo(with(notNullValue(OutputStream.class)));
+            will(new org.jmock.api.Action() {
+                public void describeTo(Description description) {
+                    description.appendText("write content");
+                }
+
+                public Object invoke(Invocation invocation) throws Throwable {
+                    IOUtils.write(String.format("contents of %s", path), (OutputStream) invocation.getParameter(0));
+                    return null;
+                }
+            });
+        }});
+
+        return details;
+    }
+
+    private FileCopyDetailsInternal dir(final String path) {
+        final FileCopyDetailsInternal details = context.mock(FileCopyDetailsInternal.class, path);
+
+        context.checking(new Expectations() {{
+            allowing(details).getRelativePath();
+            will(returnValue(RelativePath.parse(false, path)));
+
+            allowing(details).getLastModified();
+            will(returnValue(1000L));
+
+            allowing(details).isDirectory();
+            will(returnValue(true));
+
+            allowing(details).getMode();
+            will(returnValue(2));
+        }});
+
+        return details;
+    }
+
+    private FileCopyDetailsInternal brokenFile(final String path, final Throwable failure) {
+        final FileCopyDetailsInternal details = context.mock(FileCopyDetailsInternal.class, String.format("[%s]", path));
+
+        context.checking(new Expectations() {{
+            allowing(details).getRelativePath();
+            will(returnValue(RelativePath.parse(true, path)));
+
+            allowing(details).getLastModified();
+            will(returnValue(1000L));
+
+            allowing(details).isDirectory();
+            will(returnValue(false));
+
+            allowing(details).getMode();
+            will(returnValue(1));
+
+            allowing(details).copyTo(with(notNullValue(OutputStream.class)));
+            will(new org.jmock.api.Action() {
+                public void describeTo(Description description) {
+                    description.appendText("write content");
+                }
+
+                public Object invoke(Invocation invocation) throws Throwable {
+                    failure.fillInStackTrace();
+                    throw failure;
+                }
+            });
+        }});
+
+        return details;
+    }
+}
diff --git a/subprojects/core/src/test/groovy/org/gradle/api/internal/file/archive/ZipCopySpecVisitorTest.java b/subprojects/core/src/test/groovy/org/gradle/api/internal/file/archive/ZipCopySpecVisitorTest.java
deleted file mode 100644
index bbc30b5..0000000
--- a/subprojects/core/src/test/groovy/org/gradle/api/internal/file/archive/ZipCopySpecVisitorTest.java
+++ /dev/null
@@ -1,245 +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.archive;
-
-import org.apache.commons.io.IOUtils;
-import org.gradle.api.GradleException;
-import org.gradle.api.file.FileVisitDetails;
-import org.gradle.api.file.RelativePath;
-import org.gradle.api.internal.file.archive.compression.ArchiveOutputStreamFactory;
-import org.gradle.api.internal.file.copy.ArchiveCopyAction;
-import org.gradle.api.internal.file.copy.ZipDeflatedCompressor;
-import org.gradle.api.internal.file.copy.ReadableCopySpec;
-import org.gradle.api.internal.file.copy.ZipStoredCompressor;
-import org.gradle.test.fixtures.file.TestFile;
-import org.gradle.test.fixtures.file.TestNameTestDirectoryProvider;
-import org.hamcrest.Description;
-import org.jmock.Expectations;
-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.OutputStream;
-import java.util.HashMap;
-import java.util.Map;
-
-import static org.gradle.api.file.FileVisitorUtil.assertVisitsPermissions;
-import static org.hamcrest.Matchers.*;
-import static org.junit.Assert.assertThat;
-import static org.junit.Assert.fail;
-
- at RunWith(JMock.class)
-public class ZipCopySpecVisitorTest {
-    @Rule
-    public final TestNameTestDirectoryProvider tmpDir = new TestNameTestDirectoryProvider();
-    private final JUnit4Mockery context = new JUnit4Mockery();
-    private final ArchiveCopyAction copyAction = context.mock(ZipCopyAction.class);
-    private final ReadableCopySpec copySpec = context.mock(ReadableCopySpec.class);
-    private final ZipCopySpecVisitor visitor = new ZipCopySpecVisitor();
-    private TestFile zipFile;
-
-    @Before
-    public void setup() {
-        zipFile = tmpDir.getTestDirectory().file("test.zip");
-        context.checking(new Expectations() {{
-            allowing(copyAction).getArchivePath();
-            will(returnValue(zipFile));
-        }});
-        context.checking(new Expectations() {{
-            allowing(copyAction).getCompressor();
-            will(returnValue(ZipStoredCompressor.INSTANCE));
-        }});
-    }
-
-    private TestFile initializeZipFile(final TestFile testFile, final ArchiveOutputStreamFactory compressor) {
-        context.checking(new Expectations() {{
-            allowing(copyAction).getArchivePath();
-            will(returnValue(zipFile));
-            allowing(copyAction).getCompressor();
-            will(returnValue(compressor));
-        }});
-        return testFile;
-    }
-
-    @Test
-    public void createsZipFile() {
-        initializeZipFile(zipFile, ZipStoredCompressor.INSTANCE);
-        zip(dir("dir"), file("dir/file1"), file("file2"));
-
-        TestFile expandDir = tmpDir.getTestDirectory().file("expanded");
-        zipFile.unzipTo(expandDir);
-        expandDir.file("dir/file1").assertContents(equalTo("contents of dir/file1"));
-        expandDir.file("file2").assertContents(equalTo("contents of file2"));
-    }
-
-    @Test
-    public void createsDeflatedZipFile() {
-        initializeZipFile(zipFile, ZipDeflatedCompressor.INSTANCE);
-        zip(dir("dir"), file("dir/file1"), file("file2"));
-
-        TestFile expandDir = tmpDir.getTestDirectory().file("expanded");
-        zipFile.unzipTo(expandDir);
-        expandDir.file("dir/file1").assertContents(equalTo("contents of dir/file1"));
-        expandDir.file("file2").assertContents(equalTo("contents of file2"));
-    }
-
-    @Test
-    public void zipFileContainsExpectedPermissions() {
-        zip(dir("dir"), file("file"));
-
-        Map<String, Integer> expected = new HashMap<String, Integer>();
-        expected.put("dir", 2);
-        expected.put("file", 1);
-
-        assertVisitsPermissions(new ZipFileTree(zipFile, null), expected);
-    }
-
-    @Test
-    public void wrapsFailureToOpenOutputFile() {
-        final TestFile invalidZipFile = tmpDir.createDir("test.zip");
-
-        context.checking(new Expectations() {{
-            allowing(copyAction).getArchivePath();
-            will(returnValue(invalidZipFile));
-        }});
-
-        try {
-            visitor.startVisit(copyAction);
-            fail();
-        } catch (GradleException e) {
-            assertThat(e.getMessage(), equalTo(String.format("Could not create ZIP '%s'.", zipFile)));
-        }
-    }
-
-    @Test
-    public void wrapsFailureToAddElement() {
-        visitor.startVisit(copyAction);
-        visitor.visitSpec(copySpec);
-
-        Throwable failure = new RuntimeException("broken");
-        try {
-            visitor.visitFile(brokenFile("dir/file1", failure));
-            fail();
-        } catch (GradleException e) {
-            assertThat(e.getMessage(), equalTo(String.format("Could not add [dir/file1] to ZIP '%s'.", zipFile)));
-            assertThat(e.getCause(), sameInstance(failure));
-        }
-    }
-
-    private void zip(FileVisitDetails... files) {
-        visitor.startVisit(copyAction);
-        visitor.visitSpec(copySpec);
-
-        for (FileVisitDetails f : files) {
-            if (f.isDirectory()) {
-                visitor.visitDir(f);
-            } else {
-                visitor.visitFile(f);
-            }
-        }
-
-        visitor.endVisit();
-    }
-
-    private FileVisitDetails file(final String path) {
-        final FileVisitDetails details = context.mock(FileVisitDetails.class, path);
-
-        context.checking(new Expectations() {{
-            allowing(details).getRelativePath();
-            will(returnValue(RelativePath.parse(true, path)));
-
-            allowing(details).getLastModified();
-            will(returnValue(1000L));
-
-            allowing(details).isDirectory();
-            will(returnValue(false));
-
-            allowing(details).getMode();
-            will(returnValue(1));
-
-            allowing(details).copyTo(with(notNullValue(OutputStream.class)));
-            will(new Action() {
-                public void describeTo(Description description) {
-                    description.appendText("write content");
-                }
-
-                public Object invoke(Invocation invocation) throws Throwable {
-                    IOUtils.write(String.format("contents of %s", path), (OutputStream) invocation.getParameter(0));
-                    return null;
-                }
-            });
-        }});
-
-        return details;
-    }
-
-    private FileVisitDetails dir(final String path) {
-        final FileVisitDetails details = context.mock(FileVisitDetails.class, path);
-
-        context.checking(new Expectations() {{
-            allowing(details).getRelativePath();
-            will(returnValue(RelativePath.parse(false, path)));
-
-            allowing(details).getLastModified();
-            will(returnValue(1000L));
-
-            allowing(details).isDirectory();
-            will(returnValue(true));
-
-            allowing(details).getMode();
-            will(returnValue(2));
-        }});
-
-        return details;
-    }
-
-    private FileVisitDetails brokenFile(final String path, final Throwable failure) {
-        final FileVisitDetails details = context.mock(FileVisitDetails.class, String.format("[%s]", path));
-
-        context.checking(new Expectations() {{
-            allowing(details).getRelativePath();
-            will(returnValue(RelativePath.parse(true, path)));
-
-            allowing(details).getLastModified();
-            will(returnValue(1000L));
-
-            allowing(details).isDirectory();
-            will(returnValue(false));
-
-            allowing(details).getMode();
-            will(returnValue(1));
-
-            allowing(details).copyTo(with(notNullValue(OutputStream.class)));
-            will(new Action() {
-                public void describeTo(Description description) {
-                    description.appendText("write content");
-                }
-
-                public Object invoke(Invocation invocation) throws Throwable {
-                    failure.fillInStackTrace();
-                    throw failure;
-                }
-            });
-        }});
-
-        return details;
-    }
-}
diff --git a/subprojects/core/src/test/groovy/org/gradle/api/internal/file/archive/compression/ArchiversTest.groovy b/subprojects/core/src/test/groovy/org/gradle/api/internal/file/archive/compression/ArchiversTest.groovy
index 7dd9b99..935df22 100644
--- a/subprojects/core/src/test/groovy/org/gradle/api/internal/file/archive/compression/ArchiversTest.groovy
+++ b/subprojects/core/src/test/groovy/org/gradle/api/internal/file/archive/compression/ArchiversTest.groovy
@@ -20,9 +20,6 @@ package org.gradle.api.internal.file.archive.compression;
 import org.gradle.api.internal.file.FileResource
 import spock.lang.Specification
 
-/**
- * by Szczepan Faber, created at: 12/13/11
- */
 public class ArchiversTest extends Specification {
 
     def "archivers have unqique URIs"() {
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
index ecaa42b..ad4e016 100644
--- 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
@@ -22,7 +22,7 @@ import org.gradle.api.internal.file.FileResolver;
 import org.gradle.api.internal.tasks.TaskResolver;
 import org.gradle.api.tasks.TaskDependency;
 import org.gradle.test.fixtures.file.TestNameTestDirectoryProvider;
-import org.gradle.util.HelperUtil;
+import org.gradle.util.TestUtil;
 import org.gradle.util.JUnit4GroovyMockery;
 import org.jmock.Expectations;
 import org.jmock.integration.junit4.JMock;
@@ -118,7 +118,7 @@ public class DefaultConfigurableFileCollectionTest {
         }});
 
         List<Character> files = toList('a');
-        Closure closure = HelperUtil.returns(files);
+        Closure closure = TestUtil.returns(files);
         collection.from(closure);
 
         assertThat(collection.getFiles(), equalTo(toLinkedSet(file1)));
@@ -130,7 +130,7 @@ public class DefaultConfigurableFileCollectionTest {
 
     @Test
     public void canUseAClosureToSpecifyASingleFile() {
-        Closure closure = HelperUtil.returns('a');
+        Closure closure = TestUtil.returns('a');
         final File file = new File("1");
 
         collection.from(closure);
@@ -145,7 +145,7 @@ public class DefaultConfigurableFileCollectionTest {
 
     @Test
     public void closureCanReturnNull() {
-        Closure closure = HelperUtil.returns(null);
+        Closure closure = TestUtil.returns(null);
 
         collection.from(closure);
 
@@ -202,7 +202,7 @@ public class DefaultConfigurableFileCollectionTest {
             will(returnValue(file2));
         }});
 
-        collection.from(HelperUtil.toClosure("{[{['src1', { ['src2'] as String[] }]}]}"));
+        collection.from(TestUtil.toClosure("{[{['src1', { ['src2'] as String[] }]}]}"));
         assertThat(collection.getFiles(), equalTo(toLinkedSet(file1, file2)));
     }
 
@@ -362,7 +362,7 @@ public class DefaultConfigurableFileCollectionTest {
 
         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)));
+        assertThat(collection.getAsFileTree().matching(TestUtil.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
index 0dcb1ea..8bdd6b5 100644
--- 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
@@ -26,6 +26,7 @@ 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.internal.reflect.Instantiator
 import org.gradle.test.fixtures.file.TestNameTestDirectoryProvider
 import org.gradle.util.JUnit4GroovyMockery
 import org.gradle.util.WrapUtil
@@ -41,12 +42,10 @@ 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);
+    Instantiator instantiator = context.mock(Instantiator)
     DefaultConfigurableFileTree fileSet
     FileResolver fileResolverStub = [resolve: {it as File}] as FileResolver
     @Rule public TestNameTestDirectoryProvider tmpDir = new TestNameTestDirectoryProvider();
@@ -62,27 +61,27 @@ class DefaultConfigurableFileTreeTest extends AbstractTestForPatternSet {
 
     @Before public void setUp() {
         super.setUp()
-        fileSet = patternSetType.newInstance(testDir, fileResolverStub, taskResolverStub)
+        fileSet = patternSetType.newInstance(testDir, fileResolverStub, taskResolverStub, instantiator)
     }
 
     @Test public void testFileSetConstructionWithBaseDir() {
-        fileSet = new DefaultConfigurableFileTree(testDir, fileResolverStub, taskResolverStub)
+        fileSet = new DefaultConfigurableFileTree(testDir, fileResolverStub, taskResolverStub, instantiator)
         assertEquals(testDir, fileSet.dir)
     }
 
     @Test public void testFileSetConstructionFromMap() {
-        fileSet = new DefaultConfigurableFileTree(fileResolverStub, taskResolverStub, dir: testDir, includes: ['include'])
+        fileSet = new DefaultConfigurableFileTree(fileResolverStub, taskResolverStub, dir: testDir, includes: ['include'], instantiator)
         assertEquals(testDir, fileSet.dir)
         assertEquals(['include'] as Set, fileSet.includes)
     }
 
     @Test(expected = InvalidUserDataException) public void testFileSetConstructionWithNoBaseDirSpecified() {
-        DefaultConfigurableFileTree fileSet = new DefaultConfigurableFileTree([:], fileResolverStub, taskResolverStub)
+        DefaultConfigurableFileTree fileSet = new DefaultConfigurableFileTree([:], fileResolverStub, taskResolverStub, instantiator)
         fileSet.contains(new File('unknown'))
     }
 
     @Test public void testFileSetConstructionWithBaseDirAsString() {
-        DefaultConfigurableFileTree fileSet = new DefaultConfigurableFileTree(fileResolverStub, taskResolverStub, dir: 'dirname')
+        DefaultConfigurableFileTree fileSet = new DefaultConfigurableFileTree(fileResolverStub, taskResolverStub, dir: 'dirname', instantiator)
         assertEquals(new File('dirname'), fileSet.dir);
     }
 
@@ -299,7 +298,7 @@ class DefaultConfigurableFileTreeTest extends AbstractTestForPatternSet {
     @Test
     public void canGetAndSetTaskDependencies() {
         FileResolver fileResolverStub = context.mock(FileResolver.class);
-        fileSet = patternSetType.newInstance(testDir, fileResolverStub, taskResolverStub)
+        fileSet = patternSetType.newInstance(testDir, fileResolverStub, taskResolverStub, instantiator)
 
         assertThat(fileSet.getBuiltBy(), isEmpty());
 
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
index 9cb0f03..11a4694 100644
--- 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
@@ -16,8 +16,8 @@
 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.copy.CopySpecVisitor;
 import org.gradle.api.specs.Spec;
 import org.gradle.api.tasks.util.PatternSet;
 import org.gradle.test.fixtures.file.TestFile;
@@ -51,11 +51,11 @@ public class DirectoryFileTreeTest {
     @Rule
     public final TestNameTestDirectoryProvider tmpDir = new TestNameTestDirectoryProvider();
     private JUnit4Mockery context = new JUnit4GroovyMockery();
-    private CopySpecVisitor visitor;
+    private FileVisitor visitor;
 
     @Before
     public void setUp() {
-        visitor = context.mock(CopySpecVisitor.class);
+        visitor = context.mock(FileVisitor.class);
     }
 
     @Test
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
index 9eb4a54..ebe56be 100644
--- 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
@@ -18,7 +18,7 @@ package org.gradle.api.internal.file.collections;
 import groovy.lang.Closure;
 import org.gradle.test.fixtures.file.TestFile;
 import org.gradle.test.fixtures.file.TestNameTestDirectoryProvider;
-import org.gradle.util.HelperUtil;
+import org.gradle.util.TestUtil;
 import org.junit.Rule;
 import org.junit.Test;
 
@@ -45,7 +45,7 @@ public class MapFileTreeTest {
     
     @Test
     public void canAddAnElementUsingAClosureToGeneratedContent() {
-        Closure closure = HelperUtil.toClosure("{it.write('content'.getBytes())}");
+        Closure closure = TestUtil.toClosure("{it.write('content'.getBytes())}");
         tree.add("path/file.txt", closure);
 
         assertVisits(tree, toList("path/file.txt"), toList("path"));
@@ -57,7 +57,7 @@ public class MapFileTreeTest {
 
     @Test
     public void canAddMultipleElementsInDifferentDirs() {
-        Closure closure = HelperUtil.toClosure("{it.write('content'.getBytes())}");
+        Closure closure = TestUtil.toClosure("{it.write('content'.getBytes())}");
         tree.add("path/file.txt", closure);
         tree.add("file.txt", closure);
         tree.add("path/subdir/file.txt", closure);
@@ -68,7 +68,7 @@ public class MapFileTreeTest {
 
     @Test
     public void canStopVisitingElements() {
-        Closure closure = HelperUtil.toClosure("{it.write('content'.getBytes())}");
+        Closure closure = TestUtil.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/copy/CopyActionExecuterTest.groovy b/subprojects/core/src/test/groovy/org/gradle/api/internal/file/copy/CopyActionExecuterTest.groovy
new file mode 100644
index 0000000..23bc6dc
--- /dev/null
+++ b/subprojects/core/src/test/groovy/org/gradle/api/internal/file/copy/CopyActionExecuterTest.groovy
@@ -0,0 +1,74 @@
+/*
+ * Copyright 2009 the original author or authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.gradle.api.internal.file.copy
+
+import org.gradle.api.Action
+import org.gradle.api.file.FileCopyDetails
+import org.gradle.api.internal.file.BaseDirFileResolver
+import org.gradle.api.internal.file.CopyActionProcessingStreamAction
+import org.gradle.api.internal.tasks.SimpleWorkResult
+import org.gradle.api.tasks.WorkResult
+import org.gradle.internal.nativeplatform.filesystem.FileSystems
+import org.gradle.internal.reflect.DirectInstantiator
+import org.gradle.test.fixtures.file.WorkspaceTest
+
+class CopyActionExecuterTest extends WorkspaceTest {
+
+    def "correctly executes copy actions, normalising and handling excludes"() {
+        given:
+        file("a").with {
+            createFile("a")
+        }
+        file("b").with {
+            createFile("b")
+            createDir("b1").createFile("b1")
+        }
+
+        def resolver = new BaseDirFileResolver(FileSystems.getDefault(), testDirectory)
+        def copySpec = new DestinationRootCopySpec(resolver, new DefaultCopySpec(resolver, new DirectInstantiator()))
+        copySpec.with {
+            into "out"
+            from "a", {
+                from "b/b1", {
+                    it.eachFile {
+                        FileCopyDetails fcd -> fcd.exclude()
+                    }
+                }
+            }
+        }
+
+        def action = Mock(CopyActionProcessingStreamAction)
+        def workResult = true
+        def copyAction = new CopyAction() {
+            WorkResult execute(CopyActionProcessingStream stream) {
+                stream.process(action)
+                new SimpleWorkResult(workResult)
+            }
+        }
+        def executer = new CopyActionExecuter(new DirectInstantiator(), FileSystems.getDefault())
+
+        when:
+        executer.execute(copySpec, copyAction)
+
+        then:
+        1 * action.processFile({ it.relativePath.pathString == "a" })
+        0 * action.processFile(_)
+    }
+
+    Closure path(path) {
+        return { println it.relativePath.pathString; it.relativePath.pathString == path }
+    }
+}
diff --git a/subprojects/core/src/test/groovy/org/gradle/api/internal/file/copy/CopyActionImplTest.groovy b/subprojects/core/src/test/groovy/org/gradle/api/internal/file/copy/CopyActionImplTest.groovy
deleted file mode 100644
index d2024f5..0000000
--- a/subprojects/core/src/test/groovy/org/gradle/api/internal/file/copy/CopyActionImplTest.groovy
+++ /dev/null
@@ -1,95 +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.copy
-
-import org.gradle.api.file.FileTree
-import org.gradle.api.internal.file.FileResolver
-import spock.lang.Specification
-
-public class CopyActionImplTest extends Specification {
-    FileCopySpecVisitor visitor = Mock()
-    FileResolver resolver = Mock()
-    FileTree sourceFileTree = Mock()
-    CopyActionImpl copyAction = new CopyActionImpl(resolver, visitor)
-
-    def delegatesToMainSpecRootSpec() {
-        when:
-        copyAction.include 'a'
-
-        then:
-        copyAction.includes == ['a'] as Set
-        copyAction.mainSpec.includes == ['a'] as Set
-        copyAction.rootSpec.includes == [] as Set
-        copyAction.rootSpec.childSpecs.contains(copyAction.mainSpec)
-    }
-
-    def didWorkDelegatesToVisitor() {
-        when:
-        def didWork = copyAction.didWork
-
-        then:
-        1 * visitor.didWork >> true
-        didWork
-    }
-
-    def visitsAndCopiesEachSpec() {
-        FileTree source = Mock()
-        _ * source.matching(_) >> source
-
-        copyAction.from('source1')
-        def child = copyAction.from('source2') { }
-
-        when:
-        copyAction.execute()
-
-        then:
-        1 * visitor.startVisit(copyAction)
-        1 * visitor.visitSpec(copyAction.rootSpec)
-        1 * resolver.resolveFilesAsTree([[] as Set] as Object[]) >> source
-        1 * visitor.visitSpec(copyAction.mainSpec)
-        1 * resolver.resolveFilesAsTree([['source1'] as Set] as Object[]) >> source
-        1 * visitor.visitSpec(child)
-        1 * resolver.resolveFilesAsTree([['source2'] as Set] as Object[]) >> source
-        1 * visitor.endVisit()
-        0 * resolver._
-        0 * visitor._
-    }
-   
-    def allSourceIncludesSourceFromAllSpecs() {
-        FileTree mainSource = Mock()
-        _ * mainSource.matching(_) >> mainSource
-        FileTree rootSource = Mock()
-        _ * rootSource.matching(_) >> rootSource
-        FileTree childSource = Mock()
-        _ * childSource.matching(_) >> childSource
-        FileTree allSource = Mock()
-
-        copyAction.from('source1')
-        copyAction.from('source2') { }
-
-        when:
-        def source = copyAction.allSource
-
-        then:
-        source == allSource
-        1 * resolver.resolveFilesAsTree([[] as Set] as Object[]) >> rootSource
-        1 * resolver.resolveFilesAsTree([['source1'] as Set] as Object[]) >> mainSource
-        1 * resolver.resolveFilesAsTree([['source2'] as Set] as Object[]) >> childSource
-        1 * resolver.resolveFilesAsTree([[rootSource, mainSource, childSource]] as Object[]) >> allSource
-        0 * resolver._
-        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
deleted file mode 100644
index 089d334..0000000
--- a/subprojects/core/src/test/groovy/org/gradle/api/internal/file/copy/CopySpecImplTest.groovy
+++ /dev/null
@@ -1,350 +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.copy
-
-import org.apache.tools.ant.filters.HeadFilter
-import org.apache.tools.ant.filters.StripJavaComments
-import org.gradle.api.Action
-import org.gradle.api.file.FileTree
-import org.gradle.api.file.RelativePath
-import org.gradle.api.internal.file.FileResolver
-import org.gradle.api.specs.Spec
-import org.gradle.api.tasks.util.PatternSet
-import org.gradle.test.fixtures.file.TestFile
-import org.gradle.test.fixtures.file.TestNameTestDirectoryProvider
-import org.gradle.util.JUnit4GroovyMockery
-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.*
-
- at RunWith(JMock)
-public class CopySpecImplTest {
-
-    @Rule public TestNameTestDirectoryProvider testDir = new TestNameTestDirectoryProvider();
-    private TestFile baseFile = testDir.testDirectory
-    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 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 testHasNoPermissionsByDefault() {
-        assert spec.fileMode == null
-        assert spec.dirMode == null
-    }
-
-    @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/CopySpecMatchingTest.groovy b/subprojects/core/src/test/groovy/org/gradle/api/internal/file/copy/CopySpecMatchingTest.groovy
new file mode 100644
index 0000000..03b9f1d
--- /dev/null
+++ b/subprojects/core/src/test/groovy/org/gradle/api/internal/file/copy/CopySpecMatchingTest.groovy
@@ -0,0 +1,88 @@
+/*
+ * Copyright 2013 the original author or authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY 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.Action
+import org.gradle.api.file.FileCopyDetails
+import org.gradle.api.file.FileTree
+import org.gradle.api.file.RelativePath
+import org.gradle.api.internal.Actions
+import org.gradle.api.internal.file.TestFiles
+import org.gradle.internal.reflect.DirectInstantiator
+import spock.lang.Specification
+
+class CopySpecMatchingTest extends Specification {
+
+    DefaultCopySpec copySpec = new DefaultCopySpec(TestFiles.resolver(), new DirectInstantiator(), null)
+
+    FileTree fileTree = Mock()
+
+    def canMatchFiles() {
+        given:
+
+        FileCopyDetails details1 = Mock()
+        FileCopyDetails details2 = Mock()
+
+        details1.relativePath >>  RelativePath.parse(true, 'path/abc.txt')
+        details2.relativePath >> RelativePath.parse(true, 'path/bcd.txt')
+
+        Action matchingAction = Mock()
+
+        when:
+        copySpec.filesMatching("**/a*", matchingAction)
+        copySpec.allCopyActions.each { copyAction ->
+            copyAction.execute(details1)
+            copyAction.execute(details2)
+        }
+
+        then:
+        1 * matchingAction.execute(details1)
+    }
+
+
+    def canNotMatchFiles() {
+        given:
+
+        FileCopyDetails details1 = Mock()
+        FileCopyDetails details2 = Mock()
+
+        details1.relativePath >>  RelativePath.parse(true, 'path/abc.txt')
+        details2.relativePath >> RelativePath.parse(true, 'path/bcd.txt')
+
+        Action matchingAction = Mock()
+
+        when:
+        copySpec.filesNotMatching("**/a*", matchingAction)
+        copySpec.allCopyActions.each { copyAction ->
+            copyAction.execute(details1)
+            copyAction.execute(details2)
+        }
+
+        then:
+        1 * matchingAction.execute(details2)
+    }
+
+    def matchingSpecInherited() {
+        given:
+        DefaultCopySpec childSpec = copySpec.addChild()
+        when:
+        copySpec.filesMatching("**/*.java", Actions.doNothing())
+        then:
+        1 == childSpec.allCopyActions.size()
+        childSpec.allCopyActions[0] instanceof MatchingCopyAction
+    }
+}
diff --git a/subprojects/core/src/test/groovy/org/gradle/api/internal/file/copy/DefaultCopySpecTest.groovy b/subprojects/core/src/test/groovy/org/gradle/api/internal/file/copy/DefaultCopySpecTest.groovy
new file mode 100644
index 0000000..1060738
--- /dev/null
+++ b/subprojects/core/src/test/groovy/org/gradle/api/internal/file/copy/DefaultCopySpecTest.groovy
@@ -0,0 +1,394 @@
+/*
+ * Copyright 2010 the original author or authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY 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.Action
+import org.gradle.api.file.DuplicatesStrategy
+import org.gradle.api.file.FileTree
+import org.gradle.api.file.RelativePath
+import org.gradle.api.internal.Actions
+import org.gradle.api.internal.file.FileResolver
+import org.gradle.api.specs.Spec
+import org.gradle.api.tasks.util.PatternSet
+import org.gradle.internal.reflect.DirectInstantiator
+import org.gradle.internal.reflect.Instantiator
+import org.gradle.test.fixtures.file.TestFile
+import org.gradle.test.fixtures.file.TestNameTestDirectoryProvider
+import org.gradle.util.JUnit4GroovyMockery
+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.*
+
+ at RunWith(JMock)
+public class DefaultCopySpecTest {
+
+    @Rule public TestNameTestDirectoryProvider testDir = new TestNameTestDirectoryProvider();
+    private TestFile baseFile = testDir.testDirectory
+    private final JUnit4GroovyMockery context = new JUnit4GroovyMockery();
+    private final FileResolver fileResolver = context.mock(FileResolver);
+    private final Instantiator instantiator = new DirectInstantiator()
+    private final DefaultCopySpec spec = new DefaultCopySpec(fileResolver, instantiator)
+
+    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() {
+        DefaultCopySpec child = spec.from('source') {
+        }
+
+        assertThat(child, not(sameInstance(spec)))
+        assertEquals(['source'], child.sourcePaths as List);
+    }
+
+    @Test public void testMultipleSourcesWithClosure() {
+        DefaultCopySpec 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() {
+        DefaultCopySpec other1 = new DefaultCopySpec(fileResolver, instantiator)
+        DefaultCopySpec other2 = new DefaultCopySpec(fileResolver, instantiator)
+
+        spec.with other1, other2
+        assertTrue(spec.sourcePaths.empty)
+        assertThat(spec.childSpecs.size(), equalTo(2))
+    }
+
+    @Test public void testWithSpecInheritsDestinationPathFromParent() {
+        DefaultCopySpec other = new DefaultCopySpec(fileResolver, instantiator)
+        other.into 'other'
+
+        spec.into 'spec'
+        spec.with other
+
+        CopySpecInternal child = spec.childSpecs[0]
+        assertThat(child.destPath, equalTo(new RelativePath(false, 'spec', 'other')))
+    }
+
+    @Test public void testDestinationWithClosure() {
+        DefaultCopySpec child = spec.into('target') {
+        }
+
+        assertThat(child, not(sameInstance(spec)))
+        assertThat(child.destPath, equalTo(new RelativePath(false, 'target')))
+    }
+
+    @Test public void testRootSpecHasRootPathAsDestination() {
+        assertThat(spec.destPath, equalTo(new RelativePath(false)))
+    }
+
+    @Test public void testChildSpecResolvesIntoArgRelativeToParentDestinationDir() {
+        DefaultCopySpec child = spec.from('somedir') { into 'child' }
+        assertThat(child.destPath, equalTo(new RelativePath(false, 'child')))
+
+        DefaultCopySpec 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() {
+        DefaultCopySpec child = spec.from('somedir') { }
+        assertThat(child.destPath, equalTo(spec.destPath))
+
+        child.into 'child'
+
+        DefaultCopySpec 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() {
+        DefaultCopySpec 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() {
+        DefaultCopySpec 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 caseSensitiveFlagDefaultsToTrue() {
+        assert spec.caseSensitive
+        assert spec.patternSet.caseSensitive
+    }
+
+    @Test public void childUsesCaseSensitiveFlagFromParentAsDefault() {
+        def child = spec.from('dir') {}
+
+        assert child.caseSensitive
+        assert child.patternSet.caseSensitive
+
+        spec.caseSensitive = false
+        assert !child.caseSensitive
+        assert !child.patternSet.caseSensitive
+
+        child.caseSensitive = true
+        assert child.caseSensitive
+        assert child.patternSet.caseSensitive
+    }
+
+    @Test public void includeEmptyDirsFlagDefaultsToTrue() {
+        assert spec.includeEmptyDirs
+    }
+
+    @Test public void childUsesIncludeEmptyDirsFlagFromParentAsDefault() {
+        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
+        DefaultCopySpec childSpec = spec.from('src') {
+            eachFile childAction
+        }
+
+        assertThat(childSpec.allCopyActions, equalTo([parentAction, childAction]))
+    }
+
+    @Test public void testHasNoPermissionsByDefault() {
+        assert spec.fileMode == null
+        assert spec.dirMode == null
+    }
+
+    @Test public void testInheritsPermissionsFromParent() {
+        spec.fileMode = 0x1
+        spec.dirMode = 0x2
+
+        DefaultCopySpec 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())
+    }
+
+    @Test public void duplicatesStrategyDefaultsToInclude() {
+        assert spec.duplicatesStrategy == DuplicatesStrategy.INCLUDE
+    }
+
+    @Test public void childInheritsDuplicatesStrategyFromParent() {
+        def child = spec.from('dir') {}
+
+        assert child.duplicatesStrategy == DuplicatesStrategy.INCLUDE
+
+        spec.duplicatesStrategy = 'EXCLUDE'
+        assert child.duplicatesStrategy == DuplicatesStrategy.EXCLUDE
+
+        child.duplicatesStrategy = 'INCLUDE'
+        assert child.duplicatesStrategy == DuplicatesStrategy.INCLUDE
+    }
+
+    @Test public void testMatchingCreatesAppropriateAction() {
+        spec.filesMatching "root/**/a*", Actions.doNothing()
+        assertEquals(1, spec.allCopyActions.size())
+        assertThat(spec.allCopyActions[0], instanceOf(MatchingCopyAction))
+
+        Spec<RelativePath> matchSpec = spec.allCopyActions[0].matchSpec
+        assertTrue(matchSpec.isSatisfiedBy(RelativePath.parse(true, '/root/folder/abc')))
+        assertTrue(matchSpec.isSatisfiedBy(RelativePath.parse(true, '/root/abc')))
+        assertFalse(matchSpec.isSatisfiedBy(RelativePath.parse(true, '/notRoot/abc')))
+        assertFalse(matchSpec.isSatisfiedBy(RelativePath.parse(true, '/root/bbc')))
+        assertFalse(matchSpec.isSatisfiedBy(RelativePath.parse(true, '/notRoot/bbc')))
+    }
+
+    @Test public void testNotMatchingCreatesAppropriateAction() {
+        // no path component starting with an a
+        spec.filesNotMatching("**/a*/**", Actions.doNothing())
+        assertEquals(1, spec.allCopyActions.size())
+        assertThat(spec.allCopyActions[0], instanceOf(MatchingCopyAction))
+
+        Spec<RelativePath> matchSpec = spec.allCopyActions[0].matchSpec
+        assertTrue(matchSpec.isSatisfiedBy(RelativePath.parse(true, 'root/folder1/folder2')))
+        assertTrue(matchSpec.isSatisfiedBy(RelativePath.parse(true, 'modules/project1')))
+        assertFalse(matchSpec.isSatisfiedBy(RelativePath.parse(true, 'archives/folder/file')))
+        assertFalse(matchSpec.isSatisfiedBy(RelativePath.parse(true, 'root/archives/file')))
+        assertFalse(matchSpec.isSatisfiedBy(RelativePath.parse(true, 'root/folder/abc')))
+    }
+
+}
+
diff --git a/subprojects/core/src/test/groovy/org/gradle/api/internal/file/copy/DeleteActionImplTest.groovy b/subprojects/core/src/test/groovy/org/gradle/api/internal/file/copy/DeleteActionImplTest.groovy
index b5f5439..bb13cca 100644
--- a/subprojects/core/src/test/groovy/org/gradle/api/internal/file/copy/DeleteActionImplTest.groovy
+++ b/subprojects/core/src/test/groovy/org/gradle/api/internal/file/copy/DeleteActionImplTest.groovy
@@ -21,9 +21,6 @@ import org.gradle.test.fixtures.file.TestNameTestDirectoryProvider
 import org.junit.Rule
 import spock.lang.Specification
 
-/**
- * @author Hans Dockter
- */
 class DeleteActionImplTest extends Specification {
     @Rule
     TestNameTestDirectoryProvider tmpDir = new TestNameTestDirectoryProvider()
diff --git a/subprojects/core/src/test/groovy/org/gradle/api/internal/file/copy/DuplicateHandlingCopyActionDecoratorTest.groovy b/subprojects/core/src/test/groovy/org/gradle/api/internal/file/copy/DuplicateHandlingCopyActionDecoratorTest.groovy
new file mode 100644
index 0000000..7fbb8c7
--- /dev/null
+++ b/subprojects/core/src/test/groovy/org/gradle/api/internal/file/copy/DuplicateHandlingCopyActionDecoratorTest.groovy
@@ -0,0 +1,196 @@
+/*
+ * Copyright 2010 the original author or authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY 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.Action
+import org.gradle.api.file.*
+import org.gradle.api.internal.ClosureBackedAction
+import org.gradle.api.internal.ThreadGlobalInstantiator
+import org.gradle.api.internal.file.CopyActionProcessingStreamAction
+import org.gradle.api.internal.tasks.SimpleWorkResult
+import org.gradle.api.tasks.WorkResult
+import org.gradle.internal.nativeplatform.filesystem.FileSystem
+import org.gradle.internal.reflect.Instantiator
+import org.gradle.logging.ConfigureLogging
+import org.gradle.logging.TestAppender
+import org.junit.Rule
+import spock.lang.Shared
+import spock.lang.Specification
+
+class DuplicateHandlingCopyActionDecoratorTest extends Specification {
+
+    private static interface MyCopySpec extends CopySpec, CopySpecInternal {}
+
+    def fileSystem = Mock(FileSystem)
+    def delegateAction = Mock(CopyActionProcessingStreamAction)
+    def delegate = new CopyAction() {
+        WorkResult execute(CopyActionProcessingStream stream) {
+            stream.process(delegateAction)
+            return new SimpleWorkResult(true)
+        }
+    }
+
+    def appender = new TestAppender()
+    @Rule ConfigureLogging logging = new ConfigureLogging(appender)
+
+    @Shared Instantiator instantiator = ThreadGlobalInstantiator.getOrCreate()
+    def driver = new CopyActionExecuter(instantiator, fileSystem)
+    def copySpec = Mock(MyCopySpec) {
+        getChildren() >> []
+    }
+
+    def duplicatesIncludedByDefault() {
+        given:
+        files 'path/file1.txt', 'path/file2.txt', 'path/file1.txt'
+        actions {}
+
+        when:
+        visit()
+
+        then:
+        2 * delegateAction.processFile({ it.relativePath.pathString == '/root/path/file1.txt' })
+        1 * delegateAction.processFile({ it.relativePath.pathString == '/root/path/file2.txt' })
+    }
+
+    def duplicatesExcludedByPerFileConfiguration() {
+        given:
+        files 'path/file1.txt', 'path/file2.txt', 'path/file1.txt'
+        actions { it.duplicatesStrategy = 'exclude' }
+
+        when:
+        visit()
+
+        then:
+        1 * delegateAction.processFile({ FileCopyDetails it ->
+            it.relativePath.pathString == '/root/path/file1.txt'
+        })
+        1 * delegateAction.processFile({ it.relativePath.pathString == '/root/path/file2.txt' })
+    }
+
+
+    def duplicatesExcludedEvenWhenRenamed() {
+        given:
+        files 'module1/path/file1.txt', 'module1/path/file2.txt', 'module2/path/file1.txt'
+
+        actions({ it.name = it.name.replaceAll('module[0-9]+/', '') }, { it.duplicatesStrategy = 'exclude' })
+
+        when:
+        visit()
+
+        then:
+        1 * delegateAction.processFile({ it.relativePath.pathString == '/root/path/file1.txt' })
+        1 * delegateAction.processFile({ it.relativePath.pathString == '/root/path/file2.txt' })
+    }
+
+    def duplicatesExcludedByDefaultConfiguration() {
+        given:
+        files 'path/file1.txt', 'path/file2.txt', 'path/file1.txt'
+        actions {}
+        copySpec.duplicatesStrategy >> DuplicatesStrategy.EXCLUDE
+
+        when:
+        visit()
+
+        then:
+        1 * delegateAction.processFile({ it.relativePath.pathString == '/root/path/file1.txt' })
+        1 * delegateAction.processFile({ it.relativePath.pathString == '/root/path/file2.txt' })
+    }
+
+    def duplicatesFailByDefaultConfiguration() {
+        given:
+        files 'path/file1.txt', 'path/file2.txt', 'path/file1.txt'
+        actions {}
+        copySpec.duplicatesStrategy >> DuplicatesStrategy.FAIL
+
+        when:
+        visit()
+
+        then:
+        1 * delegateAction.processFile({ it.relativePath.pathString == '/root/path/file1.txt' })
+        1 * delegateAction.processFile({ it.relativePath.pathString == '/root/path/file2.txt' })
+        thrown(DuplicateFileCopyingException)
+    }
+
+    def duplicatesWarnByDefaultConfiguration() {
+        given:
+        files 'path/file1.txt', 'path/file2.txt', 'path/file1.txt'
+        actions {}
+        copySpec.duplicatesStrategy >> DuplicatesStrategy.WARN
+
+        when:
+        visit()
+
+        then:
+        2 * delegateAction.processFile({ it.relativePath.pathString == '/root/path/file1.txt' })
+        1 * delegateAction.processFile({ it.relativePath.pathString == '/root/path/file2.txt' })
+        appender.toString().contains('WARN Encountered duplicate path "/root/path/file1.txt"')
+    }
+
+
+    def duplicatesWarnByPerFileConfiguration() {
+        given:
+        files 'path/file1.txt', 'path/file2.txt', 'path/file1.txt'
+        actions { it.duplicatesStrategy = 'warn' }
+
+        when:
+        visit()
+
+        then:
+        2 * delegateAction.processFile({ it.relativePath.pathString == '/root/path/file1.txt' })
+        1 * delegateAction.processFile({ it.relativePath.pathString == '/root/path/file2.txt' })
+        appender.toString().contains('WARN Encountered duplicate path "/root/path/file1.txt"')
+    }
+
+    def duplicatesFailByPerFileConfiguration() {
+        given:
+        files 'path/file1.txt', 'path/file2.txt', 'path/file1.txt'
+        actions { it.duplicatesStrategy = 'fail' }
+
+        when:
+        visit()
+
+        then:
+        1 * delegateAction.processFile({ it.relativePath.pathString == '/root/path/file1.txt' })
+        1 * delegateAction.processFile({ it.relativePath.pathString == '/root/path/file2.txt' })
+        thrown(DuplicateFileCopyingException)
+    }
+
+
+    void files(String... fileNames) {
+        copySpec.destPath >> new RelativePath(false, '/root')
+        def fileTree = Mock(FileTree)
+        copySpec.getSource() >> fileTree
+        fileTree.visit(_ as FileVisitor) >> { FileVisitor visitor ->
+            fileNames.each { filename ->
+                def fvd = Mock(FileVisitDetails) {
+                    getRelativePath() >> new RelativePath(true, filename)
+                }
+                visitor.visitFile(fvd)
+            }
+            fileTree
+        }
+        copySpec.walk(_) >> { Action it -> it.execute(copySpec) }
+    }
+
+    void actions(Closure... actions) {
+        copySpec.allCopyActions >> actions.collect { new ClosureBackedAction<>(it) }
+    }
+
+    void visit() {
+        driver.execute(copySpec, delegate)
+    }
+
+}
diff --git a/subprojects/core/src/test/groovy/org/gradle/api/internal/file/copy/FileCopyActionImplTest.java b/subprojects/core/src/test/groovy/org/gradle/api/internal/file/copy/FileCopyActionImplTest.java
deleted file mode 100644
index afca533..0000000
--- a/subprojects/core/src/test/groovy/org/gradle/api/internal/file/copy/FileCopyActionImplTest.java
+++ /dev/null
@@ -1,54 +0,0 @@
-/*
- * Copyright 2009 the original author or authors.
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- *      http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-package org.gradle.api.internal.file.copy;
-
-import org.gradle.api.internal.file.FileResolver;
-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.io.File;
-
-import static org.hamcrest.Matchers.equalTo;
-import static org.hamcrest.Matchers.nullValue;
-import static org.junit.Assert.assertThat;
-
- at RunWith(JMock.class)
-public class FileCopyActionImplTest {
-    private final JUnit4Mockery context = new JUnit4Mockery();
-    private final FileResolver fileResolver = context.mock(FileResolver.class);
-    private final FileCopyActionImpl spec = new FileCopyActionImpl(fileResolver, context.mock(CopySpecVisitor.class));
-
-    @Test public void testRootSpecResolvesItsIntoArgAsDestinationDir() {
-        final File file = new File("base dir");
-
-        spec.into("somedir");
-
-        context.checking(new Expectations() {{
-            allowing(fileResolver).resolve("somedir");
-            will(returnValue(file));
-        }});
-
-        assertThat(spec.getDestinationDir(), equalTo(file));
-    }
-
-    @Test public void testRootSpecHasNoDefaultDestinationDir() {
-        assertThat(spec.getDestinationDir(), nullValue());
-    }
-
-}
diff --git a/subprojects/core/src/test/groovy/org/gradle/api/internal/file/copy/FileCopyActionTest.java b/subprojects/core/src/test/groovy/org/gradle/api/internal/file/copy/FileCopyActionTest.java
new file mode 100644
index 0000000..85981fe
--- /dev/null
+++ b/subprojects/core/src/test/groovy/org/gradle/api/internal/file/copy/FileCopyActionTest.java
@@ -0,0 +1,67 @@
+/*
+ * Copyright 2009 the original author or authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.gradle.api.internal.file.copy;
+
+import org.gradle.api.file.RelativePath;
+import org.gradle.api.internal.file.BaseDirFileResolver;
+import org.gradle.internal.nativeplatform.filesystem.FileSystems;
+import org.gradle.test.fixtures.file.TestNameTestDirectoryProvider;
+import org.jmock.Expectations;
+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 static org.gradle.api.internal.file.copy.CopyActionExecuterUtil.visit;
+
+ at RunWith(JMock.class)
+public class FileCopyActionTest {
+    private File destDir;
+    private final JUnit4Mockery context = new JUnit4Mockery();
+
+    @Rule
+    public TestNameTestDirectoryProvider tmpDir = new TestNameTestDirectoryProvider();
+
+    @Before
+    public void setUp() throws IOException {
+        destDir = tmpDir.getTestDirectory().file("dest");
+    }
+
+    @Test
+    public void plainCopy() {
+        FileCopyAction visitor = new FileCopyAction(new BaseDirFileResolver(FileSystems.getDefault(), destDir));
+        visit(visitor,
+                file(new RelativePath(true, "rootfile.txt"), new File(destDir, "rootfile.txt")),
+                file(new RelativePath(true, "subdir", "anotherfile.txt"), new File(destDir, "subdir/anotherfile.txt"))
+        );
+    }
+
+    private FileCopyDetailsInternal file(final RelativePath relativePath, final File targetFile) {
+        final FileCopyDetailsInternal details = context.mock(FileCopyDetailsInternal.class, relativePath.getPathString());
+        context.checking(new Expectations() {{
+            allowing(details).getRelativePath();
+            will(returnValue(relativePath));
+            one(details).copyTo(targetFile);
+        }});
+        return details;
+    }
+}
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
deleted file mode 100644
index 06345eb..0000000
--- a/subprojects/core/src/test/groovy/org/gradle/api/internal/file/copy/FileCopySpecVisitorTest.java
+++ /dev/null
@@ -1,92 +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.copy;
-
-import org.gradle.api.InvalidUserDataException;
-import org.gradle.api.file.FileVisitDetails;
-import org.gradle.api.file.RelativePath;
-import org.gradle.test.fixtures.file.TestNameTestDirectoryProvider;
-import org.jmock.Expectations;
-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 static org.hamcrest.Matchers.equalTo;
-import static org.junit.Assert.assertThat;
-import static org.junit.Assert.fail;
-
- at RunWith(JMock.class)
-public class FileCopySpecVisitorTest {
-    private File destDir;
-    private final JUnit4Mockery context = new JUnit4Mockery();
-    private final FileCopySpecVisitor visitor = new FileCopySpecVisitor();
-    @Rule
-    public TestNameTestDirectoryProvider tmpDir = new TestNameTestDirectoryProvider();
-
-    @Before
-    public void setUp() throws IOException {
-        destDir = tmpDir.getTestDirectory().file("dest");
-    }
-
-    @Test
-    public void plainCopy() {
-        visitor.startVisit(action(destDir));
-
-        visitor.visitDir(file(new RelativePath(false), destDir));
-
-        visitor.visitFile(file(new RelativePath(true, "rootfile.txt"), new File(destDir, "rootfile.txt")));
-
-        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")));
-    }
-
-    @Test
-    public void testThrowsExceptionWhenNoDestinationSet() {
-        try {
-            visitor.startVisit(action(null));
-            fail();
-        } catch (InvalidUserDataException e) {
-            assertThat(e.getMessage(), equalTo("No copy destination directory has been specified, use 'into' to specify a target directory."));
-        }
-    }
-
-    private FileCopyAction action(final File destDir) {
-        final FileCopyAction action = context.mock(FileCopyAction.class);
-        context.checking(new Expectations(){{
-            allowing(action).getDestinationDir();
-            will(returnValue(destDir));
-        }});
-        return action;
-    }
-
-    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));
-            one(details).copyTo(targetFile);
-        }});
-        return details;
-    }
-}
diff --git a/subprojects/core/src/test/groovy/org/gradle/api/internal/file/copy/FilterChainTest.java b/subprojects/core/src/test/groovy/org/gradle/api/internal/file/copy/FilterChainTest.java
index 019ec32..8dc07b3 100644
--- a/subprojects/core/src/test/groovy/org/gradle/api/internal/file/copy/FilterChainTest.java
+++ b/subprojects/core/src/test/groovy/org/gradle/api/internal/file/copy/FilterChainTest.java
@@ -16,7 +16,7 @@
 package org.gradle.api.internal.file.copy;
 
 import org.apache.commons.io.IOUtils;
-import org.gradle.util.HelperUtil;
+import org.gradle.util.TestUtil;
 import org.gradle.util.WrapUtil;
 import org.junit.Test;
 
@@ -59,7 +59,7 @@ public class FilterChainTest {
 
     @Test
     public void canAddLineFilterReaderToEndOfChain() {
-        filterChain.add(HelperUtil.TEST_CLOSURE);
+        filterChain.add(TestUtil.TEST_CLOSURE);
         Reader transformedReader = filterChain.transform(originalReader);
         assertThat(transformedReader, instanceOf(LineFilter.class));
     }
diff --git a/subprojects/core/src/test/groovy/org/gradle/api/internal/file/copy/MappingCopySpecVisitorTest.java b/subprojects/core/src/test/groovy/org/gradle/api/internal/file/copy/MappingCopySpecVisitorTest.java
deleted file mode 100644
index 6ddd161..0000000
--- a/subprojects/core/src/test/groovy/org/gradle/api/internal/file/copy/MappingCopySpecVisitorTest.java
+++ /dev/null
@@ -1,400 +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.copy;
-
-import org.gradle.api.Action;
-import org.gradle.api.file.FileCopyDetails;
-import org.gradle.api.file.FileVisitDetails;
-import org.gradle.api.file.RelativePath;
-import org.gradle.internal.nativeplatform.filesystem.FileSystem;
-import org.gradle.test.fixtures.file.TestFile;
-import org.gradle.test.fixtures.file.TestNameTestDirectoryProvider;
-import org.gradle.util.HelperUtil;
-import org.hamcrest.Description;
-import org.jmock.Expectations;
-import org.jmock.Sequence;
-import org.jmock.api.Invocation;
-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.ByteArrayInputStream;
-import java.io.ByteArrayOutputStream;
-import java.io.File;
-import java.io.IOException;
-
-import static org.gradle.util.Matchers.*;
-import static org.gradle.util.WrapUtil.toList;
-import static org.hamcrest.Matchers.*;
-import static org.junit.Assert.*;
-
- at RunWith(JMock.class)
-public class MappingCopySpecVisitorTest {
-    private final JUnit4Mockery context = new JUnit4Mockery();
-
-    @Rule
-    public final TestNameTestDirectoryProvider tmpDir = new TestNameTestDirectoryProvider();
-    private final CopySpecVisitor delegate = context.mock(CopySpecVisitor.class);
-    private final ReadableCopySpec spec = context.mock(ReadableCopySpec.class);
-    private final FileVisitDetails details = context.mock(FileVisitDetails.class);
-    private final FileSystem fileSystem = context.mock(FileSystem.class);
-    private final MappingCopySpecVisitor visitor = new MappingCopySpecVisitor(delegate, fileSystem);
-
-    @Test
-    public void delegatesStartAndEndVisitMethods() {
-        final CopyAction action = context.mock(CopyAction.class);
-
-        context.checking(new Expectations() {{
-            one(delegate).startVisit(action);
-            one(delegate).endVisit();
-        }});
-
-        visitor.startVisit(action);
-        visitor.endVisit();
-    }
-
-    @Test
-    public void delegatesDidWork() {
-        context.checking(new Expectations() {{
-            allowing(delegate).getDidWork();
-            will(onConsecutiveCalls(returnValue(true), returnValue(false)));
-        }});
-
-        assertTrue(visitor.getDidWork());
-        assertFalse(visitor.getDidWork());
-    }
-
-    @Test
-    public void visitFileInvokesEachCopyAction() {
-        @SuppressWarnings("unchecked")
-        final Action<FileCopyDetails> action1 = context.mock(Action.class, "action1");
-        @SuppressWarnings("unchecked")
-        final Action<FileCopyDetails> action2 = context.mock(Action.class, "action2");
-        final Collector<FileCopyDetails> collectDetails1 = collector();
-        final Collector<Object> collectDetails2 = collector();
-        final Collector<Object> collectDetails3 = collector();
-
-        context.checking(new Expectations() {{
-            Sequence seq = context.sequence("seq");
-            one(delegate).visitSpec(spec);
-            inSequence(seq);
-
-            allowing(spec).getAllCopyActions();
-            will(returnValue(toList(action1, action2)));
-
-            one(action1).execute(with(notNullValue(FileCopyDetails.class)));
-            inSequence(seq);
-            will(collectTo(collectDetails1));
-
-            one(action2).execute(with(notNullValue(FileCopyDetails.class)));
-            inSequence(seq);
-            will(collectTo(collectDetails2));
-
-            one(delegate).visitFile(with(not(sameInstance(details))));
-            inSequence(seq);
-            will(collectTo(collectDetails3));
-        }});
-
-        visitor.visitSpec(spec);
-        visitor.visitFile(details);
-
-        assertThat(collectDetails1.get(), sameInstance(collectDetails2.get()));
-        assertThat(collectDetails1.get(), sameInstance(collectDetails3.get()));
-    }
-
-    @Test
-    public void initialRelativePathForFileIsSpecPathPlusFilePath() {
-        FileCopyDetails copyDetails = expectActionExecutedWhenFileVisited();
-
-        context.checking(new Expectations() {{
-            allowing(spec).getDestPath();
-            will(returnValue(new RelativePath(false, "spec")));
-            allowing(details).getRelativePath();
-            will(returnValue(new RelativePath(true, "file")));
-        }});
-
-        assertThat(copyDetails.getRelativePath(), equalTo(new RelativePath(true, "spec", "file")));
-    }
-
-    @Test
-    public void relativePathForDirIsSpecPathPlusFilePath() {
-        FileVisitDetails visitDetails = expectSpecAndDirVisited();
-
-        context.checking(new Expectations() {{
-            allowing(spec).getDestPath();
-            will(returnValue(new RelativePath(false, "spec")));
-            allowing(details).getRelativePath();
-            will(returnValue(new RelativePath(false, "dir")));
-        }});
-
-        assertThat(visitDetails.getRelativePath(), equalTo(new RelativePath(false, "spec", "dir")));
-    }
-
-    @Test
-    public void copyActionCanChangeFileDestinationPath() {
-        FileCopyDetails copyDetails = expectActionExecutedWhenFileVisited();
-
-        RelativePath newPath = new RelativePath(true, "new");
-        copyDetails.setRelativePath(newPath);
-        assertThat(copyDetails.getRelativePath(), equalTo(newPath));
-
-        copyDetails.setPath("/a/b");
-        assertThat(copyDetails.getRelativePath(), equalTo(new RelativePath(true, "a", "b")));
-
-        copyDetails.setName("new name");
-        assertThat(copyDetails.getRelativePath(), equalTo(new RelativePath(true, "a", "new name")));
-    }
-
-    @Test
-    public void copyActionCanExcludeFile() {
-        @SuppressWarnings("unchecked")
-        final Action<FileCopyDetails> action1 = context.mock(Action.class, "action1");
-        @SuppressWarnings("unchecked")
-        final Action<FileCopyDetails> action2 = context.mock(Action.class, "action2");
-
-        context.checking(new Expectations() {{
-            Sequence seq = context.sequence("seq");
-            one(delegate).visitSpec(spec);
-            inSequence(seq);
-
-            allowing(spec).getAllCopyActions();
-            will(returnValue(toList(action1, action2)));
-
-            one(action1).execute(with(notNullValue(FileCopyDetails.class)));
-            inSequence(seq);
-            will(excludeFile());
-        }});
-
-        visitor.visitSpec(spec);
-        visitor.visitFile(details);
-    }
-
-    @Test
-    public void copyActionCanFilterContentWhenFileIsCopiedToStream() {
-        final FileCopyDetails mappedDetails = expectActionExecutedWhenFileVisited();
-
-        context.checking(new Expectations() {{
-            one(details).open();
-            will(returnValue(new ByteArrayInputStream("content".getBytes())));
-        }});
-
-        mappedDetails.filter(HelperUtil.toClosure("{ 'PREFIX: ' + it } "));
-
-        ByteArrayOutputStream outstr = new ByteArrayOutputStream();
-        mappedDetails.copyTo(outstr);
-        assertThat(new String(outstr.toByteArray()), equalTo("PREFIX: content"));
-    }
-
-    @Test
-    public void copyActionCanFilterContentWhenFileIsCopiedToFile() {
-        final FileCopyDetails mappedDetails = expectActionExecutedWhenFileVisited();
-
-        // shortcut the permission logic by explicitly setting permissions
-        mappedDetails.setMode(0644);
-
-        context.checking(new Expectations() {{
-
-            one(details).open();
-            will(returnValue(new ByteArrayInputStream("content".getBytes())));
-            one(details).isDirectory();
-            will(returnValue(false));
-            one(details).getLastModified();
-            will(returnValue(90L));
-        }});
-
-        mappedDetails.filter(HelperUtil.toClosure("{ 'PREFIX: ' + it } "));
-
-        TestFile destDir = tmpDir.getTestDirectory().file("test.txt");
-        mappedDetails.copyTo(destDir);
-        destDir.assertContents(equalTo("PREFIX: content"));
-    }
-
-    @Test
-    public void explicitFileModeDefinitionIsAppliedToTarget() throws IOException {
-        final FileCopyDetails mappedDetails = expectActionExecutedWhenFileVisited();
-        final TestFile destFile = tmpDir.getTestDirectory().file("test.txt").createFile();
-
-        // set file permissions explicitly
-        mappedDetails.setMode(0645);
-        context.checking(new Expectations() {{
-            one(details).copyTo(destFile);
-            will(returnValue(true));
-            one(fileSystem).chmod(destFile, 0645);
-        }});
-        mappedDetails.copyTo(destFile);
-    }
-
-    @Test
-    public void getSizeReturnsSizeOfFilteredContent() {
-        final FileCopyDetails mappedDetails = expectActionExecutedWhenFileVisited();
-
-        context.checking(new Expectations() {{
-            one(details).open();
-            will(returnValue(new ByteArrayInputStream("content".getBytes())));
-        }});
-
-        mappedDetails.filter(HelperUtil.toClosure("{ 'PREFIX: ' + it } "));
-
-        assertThat(mappedDetails.getSize(), equalTo(15L));
-    }
-
-    @Test
-    public void wrappedFileElementDelegatesToSourceForRemainingMethods() {
-        final FileVisitDetails mappedDetails = expectSpecAndFileVisited();
-        final File file = new File("file");
-
-        context.checking(new Expectations() {{
-            one(details).getFile();
-            will(returnValue(file));
-        }});
-
-        assertThat(mappedDetails.getFile(), sameInstance(file));
-    }
-
-    @Test
-    public void permissionsArePreservedByDefault() {
-        FileCopyDetails copyDetails = expectActionExecutedWhenFileVisited();
-
-        context.checking(new Expectations() {{
-            one(details).isDirectory();
-            will(returnValue(true));
-
-            one(spec).getDirMode();
-            will(returnValue(null));
-
-            one(details).getMode();
-            will(returnValue(123));
-        }});
-
-        assertThat(copyDetails.getMode(), equalTo(123));
-    }
-
-    @Test
-    public void filePermissionsCanBeOverriddenBySpec() {
-        FileCopyDetails copyDetails = expectActionExecutedWhenFileVisited();
-
-        context.checking(new Expectations() {{
-            one(details).isDirectory();
-            will(returnValue(false));
-
-            one(spec).getFileMode();
-            will(returnValue(234));
-        }});
-
-        assertThat(copyDetails.getMode(), equalTo(234));
-    }
-
-
-    @Test
-    public void directoryPermissionsCanBeOverriddenBySpec() {
-        FileCopyDetails copyDetails = expectActionExecutedWhenFileVisited();
-
-        context.checking(new Expectations() {{
-            one(details).isDirectory();
-            will(returnValue(true));
-
-            one(spec).getDirMode();
-            will(returnValue(345));
-        }});
-
-        assertThat(copyDetails.getMode(), equalTo(345));
-    }
-
-    @Test
-    public void permissionsCanBeOverriddenByCopyAction() {
-        FileCopyDetails copyDetails = expectActionExecutedWhenFileVisited();
-
-        copyDetails.setMode(456);
-        assertThat(copyDetails.getMode(), equalTo(456));
-    }
-
-    private FileVisitDetails expectSpecAndFileVisited() {
-        final Collector<FileVisitDetails> collector = collector();
-
-        context.checking(new Expectations() {{
-            one(delegate).visitSpec(spec);
-
-            one(spec).getAllCopyActions();
-            will(returnValue(toList()));
-
-            one(delegate).visitFile(with(not(sameInstance(details))));
-            will(collectTo(collector));
-        }});
-
-        visitor.visitSpec(spec);
-        visitor.visitFile(details);
-        return collector.get();
-    }
-
-    private FileCopyDetails expectActionExecutedWhenFileVisited() {
-        final Collector<FileCopyDetails> collectDetails = collector();
-        @SuppressWarnings("unchecked")
-        final Action<FileCopyDetails> action = context.mock(Action.class, "action1");
-
-        context.checking(new Expectations() {{
-            Sequence seq = context.sequence("seq");
-            one(delegate).visitSpec(spec);
-            inSequence(seq);
-
-            allowing(spec).getAllCopyActions();
-            will(returnValue(toList(action)));
-
-            one(action).execute(with(notNullValue(FileCopyDetails.class)));
-            inSequence(seq);
-            will(collectTo(collectDetails));
-
-            one(delegate).visitFile(with(not(sameInstance(details))));
-            inSequence(seq);
-        }});
-
-        visitor.visitSpec(spec);
-        visitor.visitFile(details);
-
-        return collectDetails.get();
-    }
-
-    private FileVisitDetails expectSpecAndDirVisited() {
-        final Collector<FileVisitDetails> collector = collector();
-
-        context.checking(new Expectations() {{
-            one(delegate).visitSpec(spec);
-            one(delegate).visitDir(with(not(sameInstance(details))));
-
-            will(collectTo(collector));
-        }});
-
-        visitor.visitSpec(spec);
-        visitor.visitDir(details);
-
-        return collector.get();
-    }
-
-    private org.jmock.api.Action excludeFile() {
-        return new org.jmock.api.Action() {
-            public void describeTo(Description description) {
-                description.appendText("exclude file");
-            }
-
-            public Object invoke(Invocation invocation) throws Throwable {
-                FileCopyDetails details = (FileCopyDetails) invocation.getParameter(0);
-                details.exclude();
-                return null;
-            }
-        };
-    }
-}
diff --git a/subprojects/core/src/test/groovy/org/gradle/api/internal/file/copy/NormalizingCopyActionDecoratorTest.java b/subprojects/core/src/test/groovy/org/gradle/api/internal/file/copy/NormalizingCopyActionDecoratorTest.java
new file mode 100644
index 0000000..9830cf4
--- /dev/null
+++ b/subprojects/core/src/test/groovy/org/gradle/api/internal/file/copy/NormalizingCopyActionDecoratorTest.java
@@ -0,0 +1,151 @@
+/*
+ * Copyright 2009 the original author or authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.gradle.api.internal.file.copy;
+
+import org.gradle.api.file.FileCopyDetails;
+import org.gradle.api.file.RelativePath;
+import org.gradle.api.internal.file.CopyActionProcessingStreamAction;
+import org.gradle.api.internal.tasks.SimpleWorkResult;
+import org.gradle.api.tasks.WorkResult;
+import org.hamcrest.BaseMatcher;
+import org.hamcrest.Description;
+import org.hamcrest.Matcher;
+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 static org.gradle.api.internal.file.copy.CopyActionExecuterUtil.visit;
+
+ at RunWith(JMock.class)
+public class NormalizingCopyActionDecoratorTest {
+    private final JUnit4Mockery context = new JUnit4Mockery();
+    private final CopyActionProcessingStreamAction delegateAction = context.mock(CopyActionProcessingStreamAction.class);
+    private final CopyAction delegate = new CopyAction() {
+        public WorkResult execute(CopyActionProcessingStream stream) {
+            stream.process(delegateAction);
+            return new SimpleWorkResult(true);
+        }
+    };
+    private final NormalizingCopyActionDecorator decorator = new NormalizingCopyActionDecorator(delegate);
+
+    @Test
+    public void doesNotVisitADirectoryWhichHasBeenVisitedBefore() {
+        final FileCopyDetailsInternal details = file("dir", true, true);
+        final FileCopyDetailsInternal file = file("dir/file", false, true);
+
+        context.checking(new Expectations() {{
+            one(delegateAction).processFile(details);
+            one(delegateAction).processFile(file);
+        }});
+
+        visit(decorator, details, file, details);
+    }
+
+    @Test
+    public void visitsDirectoryAncestorsWhichHaveNotBeenVisited() {
+        final FileCopyDetailsInternal dir1 = file("a/b/c", true, true);
+        final FileCopyDetailsInternal file1 = file("a/b/c/file", false, true);
+
+        decorator.execute(new CopyActionProcessingStream() {
+            public void process(CopyActionProcessingStreamAction action) {
+                context.checking(new Expectations() {{
+                    one(delegateAction).processFile(with(hasPath("a")));
+                    one(delegateAction).processFile(with(hasPath("a/b")));
+                    one(delegateAction).processFile(dir1);
+                    one(delegateAction).processFile(file1);
+                }});
+
+                action.processFile(dir1);
+                action.processFile(file1);
+
+                final FileCopyDetailsInternal dir2 = file("a/b/d/e", true, true);
+                final FileCopyDetailsInternal file2 = file("a/b/d/e/file", false, true);
+
+                context.checking(new Expectations() {{
+                    one(delegateAction).processFile(with(hasPath("a/b/d")));
+                    one(delegateAction).processFile(dir2);
+                    one(delegateAction).processFile(file2);
+                }});
+
+                action.processFile(dir2);
+                action.processFile(file2);
+            }
+        });
+    }
+
+    @Test
+    public void visitsFileAncestorsWhichHaveNotBeenVisited() {
+        final FileCopyDetailsInternal details = file("a/b/c", false, true);
+
+        context.checking(new Expectations() {{
+            one(delegateAction).processFile(with(hasPath("a")));
+            one(delegateAction).processFile(with(hasPath("a/b")));
+            one(delegateAction).processFile(details);
+        }});
+
+        visit(decorator, details);
+    }
+
+    @Test
+    public void visitsAnEmptyDirectoryIfCorrespondingOptionIsOn() {
+        final FileCopyDetailsInternal dir = file("dir", true, true);
+
+        context.checking(new Expectations() {{
+            one(delegateAction).processFile(dir);
+        }});
+
+        visit(decorator, dir);
+    }
+
+    @Test
+    public void doesNotVisitAnEmptyDirectoryIfCorrespondingOptionIsOff() {
+        final FileCopyDetailsInternal dir = file("dir", true, false);
+
+        context.checking(new Expectations() {{
+            exactly(0).of(delegateAction).processFile(dir);
+        }});
+
+        visit(decorator, dir);
+    }
+
+    private FileCopyDetailsInternal file(final String path, final boolean isDir, final boolean includeEmptyDirs) {
+        final FileCopyDetailsInternal details = context.mock(FileCopyDetailsInternal.class, path);
+        context.checking(new Expectations() {{
+            allowing(details).getRelativePath();
+            will(returnValue(RelativePath.parse(false, path)));
+            allowing(details).isDirectory();
+            will(returnValue(isDir));
+            allowing(details).isIncludeEmptyDirs();
+            will(returnValue(includeEmptyDirs));
+        }});
+        return details;
+    }
+
+    private Matcher<FileCopyDetailsInternal> hasPath(final String path) {
+        return new BaseMatcher<FileCopyDetailsInternal>() {
+            public void describeTo(Description description) {
+                description.appendText("has path ").appendValue(path);
+            }
+
+            public boolean matches(Object o) {
+                FileCopyDetails details = (FileCopyDetails) o;
+                return details.getRelativePath().getPathString().equals(path);
+            }
+        };
+    }
+}
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
deleted file mode 100644
index 53e3889..0000000
--- a/subprojects/core/src/test/groovy/org/gradle/api/internal/file/copy/NormalizingCopySpecVisitorTest.java
+++ /dev/null
@@ -1,226 +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.copy;
-
-import org.gradle.api.file.FileVisitDetails;
-import org.gradle.api.file.RelativePath;
-import org.hamcrest.BaseMatcher;
-import org.hamcrest.Description;
-import org.hamcrest.Matcher;
-import org.jmock.Expectations;
-import org.jmock.integration.junit4.JUnit4Mockery;
-import org.jmock.integration.junit4.JMock;
-
-import org.junit.Test;
-import org.junit.runner.RunWith;
-
- at RunWith(JMock.class)
-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);
-    }
-
-    @Test
-    public void doesNotVisitADirectoryUntilAChildFileIsVisited() {
-        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() {{
-            one(delegate).visitDir(dir);
-            one(delegate).visitFile(file);
-        }});
-
-        visitor.visitFile(file);
-    }
-
-    @Test
-    public void doesNotVisitADirectoryUntilAChildDirIsVisited() {
-        final FileVisitDetails dir = file("dir");
-        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);
-
-        context.checking(new Expectations() {{
-            one(delegate).visitDir(dir);
-            one(delegate).visitDir(subdir);
-            one(delegate).visitFile(file);
-        }});
-
-        visitor.visitFile(file);
-    }
-
-    @Test
-    public void visitsDirectoryAncestorsWhichHaveNotBeenVisited() {
-        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);
-
-        final FileVisitDetails dir2 = file("a/b/d/e");
-        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);
-    }
-
-    @Test
-    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() {
-        allowGetIncludeEmptyDirs();
-
-        context.checking(new Expectations() {{
-            one(delegate).visitSpec(spec);
-        }});
-
-        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() {{
-            allowing(details).getRelativePath();
-            will(returnValue(RelativePath.parse(false, path)));
-        }});
-        return details;
-    }
-
-    private Matcher<FileVisitDetails> hasPath(final String path) {
-        return new BaseMatcher<FileVisitDetails>() {
-            public void describeTo(Description description) {
-                description.appendText("has path ").appendValue(path);
-            }
-
-            public boolean matches(Object o) {
-                FileVisitDetails details = (FileVisitDetails) o;
-                return details.getRelativePath().getPathString().equals(path);
-            }
-        };
-    }
-}
diff --git a/subprojects/core/src/test/groovy/org/gradle/api/internal/file/copy/SyncCopyActionDecoratorTest.groovy b/subprojects/core/src/test/groovy/org/gradle/api/internal/file/copy/SyncCopyActionDecoratorTest.groovy
new file mode 100644
index 0000000..604afe8
--- /dev/null
+++ b/subprojects/core/src/test/groovy/org/gradle/api/internal/file/copy/SyncCopyActionDecoratorTest.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.file.copy
+
+import org.gradle.api.Action
+import org.gradle.api.internal.file.BaseDirFileResolver
+import org.gradle.internal.nativeplatform.filesystem.FileSystems
+import org.gradle.internal.reflect.DirectInstantiator
+import org.gradle.test.fixtures.file.WorkspaceTest
+
+class SyncCopyActionDecoratorTest extends WorkspaceTest {
+
+    FileCopier copier
+
+    def setup() {
+        copier = new FileCopier(new DirectInstantiator(), new BaseDirFileResolver(FileSystems.getDefault(), testDirectory))
+    }
+
+    void deletesExtraFilesFromDestinationDirectoryAtTheEndOfVisit() {
+        given:
+        file("src").with {
+            createFile("subdir/included.txt")
+            createFile("included.txt")
+        }
+
+        file("dest").with {
+            createFile("subdir/included.txt")
+            createFile("subdir/extra.txt")
+            createFile("included.txt")
+            createFile("extra.txt")
+            createDir("extra")
+        }
+
+        when:
+        def result = copier.sync({
+            it.from "src"
+            it.into "dest"
+        } as Action)
+
+        then:
+        result.didWork
+        file("dest").assertHasDescendants("subdir/included.txt", "included.txt");
+    }
+
+}
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
deleted file mode 100644
index 68e806b..0000000
--- a/subprojects/core/src/test/groovy/org/gradle/api/internal/file/copy/SyncCopySpecVisitorTest.java
+++ /dev/null
@@ -1,169 +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.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.collections.DirectoryFileTree;
-import org.gradle.test.fixtures.file.TestFile;
-import org.gradle.test.fixtures.file.TestNameTestDirectoryProvider;
-import org.jmock.Expectations;
-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.lang.reflect.Field;
-import java.util.HashSet;
-import java.util.Set;
-
-import static org.hamcrest.Matchers.notNullValue;
-import static org.junit.Assert.assertTrue;
-
- at RunWith(JMock.class)
-public class SyncCopySpecVisitorTest {
-    @Rule
-    public final TestNameTestDirectoryProvider tmpDir = new TestNameTestDirectoryProvider();
-    private final JUnit4Mockery context = new JUnit4Mockery();
-    private final CopySpecVisitor delegate = context.mock(CopySpecVisitor.class);
-    private final SyncCopySpecVisitor visitor = new SyncCopySpecVisitor(delegate);
-
-    @Before
-    public void setUp() {
-        context.checking(new Expectations(){{
-            allowing(delegate).startVisit(with(notNullValue(CopyAction.class)));
-            allowing(delegate).visitFile(with(notNullValue(FileVisitDetails.class)));
-            allowing(delegate).visitDir(with(notNullValue(FileVisitDetails.class)));
-            allowing(delegate).endVisit();
-        }});
-    }
-    
-    @Test
-    public void deletesExtraFilesFromDestinationDirectoryAtTheEndOfVisit() {
-        TestFile destDir = tmpDir.createDir("dest");
-        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"));
-        visitor.visitFile(file("subdir/included.txt"));
-        visitor.visitFile(file("included.txt"));
-        visitor.endVisit();
-
-        destDir.assertHasDescendants("subdir/included.txt", "included.txt");
-    }
-
-    @Test
-    public void deletesExtraDirectoriesFromDestinationDirectoryAtTheEndOfVisit() throws Exception {
-        TestFile destDir = tmpDir.createDir("dest");
-        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<RelativePath> actual = new HashSet<RelativePath>();
-        new DirectoryFileTree(destDir).postfix().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");
-    }
-
-    @Test
-    public void doesNotDeleteDestDirectoryWhenNothingCopied() {
-        TestFile destDir = tmpDir.createDir("dest");
-        destDir.createFile("extra.txt");
-        destDir.createFile("extra/extra.txt");
-
-        visitor.startVisit(action(destDir));
-        visitor.endVisit();
-
-        destDir.assertHasDescendants();
-    }
-
-    @Test
-    public void didWorkWhenDelegateDidWork() {
-        context.checking(new Expectations() {{
-            allowing(delegate).getDidWork();
-            will(returnValue(true));
-        }});
-
-        assertTrue(visitor.getDidWork());
-    }
-
-    @Test
-    public void didWorkWhenFilesDeleted() {
-        TestFile destDir = tmpDir.createDir("dest");
-        destDir.createFile("extra.txt");
-
-        visitor.startVisit(action(destDir));
-        visitor.endVisit();
-
-        assertTrue(visitor.getDidWork());
-    }
-
-    private FileCopyAction action(final File destDir) {
-        final FileCopyAction action = context.mock(FileCopyAction.class);
-
-        context.checking(new Expectations() {{
-            allowing(action).getDestinationDir();
-            will(returnValue(destDir));
-        }});
-
-        return action;
-    }
-
-    private FileVisitDetails file(final String path) {
-        return file(RelativePath.parse(true, path));
-    }
-
-    private FileVisitDetails dir(final String path) {
-        return file(RelativePath.parse(false, path));
-    }
-
-    private FileVisitDetails file(final RelativePath relativePath) {
-        final FileVisitDetails details = context.mock(FileVisitDetails.class, relativePath.toString());
-
-        context.checking(new Expectations(){{
-            allowing(details).getRelativePath();
-            will(returnValue(relativePath));
-        }});
-
-        return details;
-    }
-}
diff --git a/subprojects/core/src/test/groovy/org/gradle/api/internal/file/pattern/RegExpPatternStepTest.java b/subprojects/core/src/test/groovy/org/gradle/api/internal/file/pattern/RegExpPatternStepTest.java
index fffdd9f..d4df917 100644
--- a/subprojects/core/src/test/groovy/org/gradle/api/internal/file/pattern/RegExpPatternStepTest.java
+++ b/subprojects/core/src/test/groovy/org/gradle/api/internal/file/pattern/RegExpPatternStepTest.java
@@ -16,11 +16,9 @@
 package org.gradle.api.internal.file.pattern;
 
 import org.junit.Test;
+
 import static org.junit.Assert.*;
 
-/**
- * @author Steve Appling
- */
 public class RegExpPatternStepTest {
 
     private void testPatternEscape(String expected, String pattern) {
diff --git a/subprojects/core/src/test/groovy/org/gradle/api/internal/initialization/DefaultScriptHandlerFactoryTest.groovy b/subprojects/core/src/test/groovy/org/gradle/api/internal/initialization/DefaultScriptHandlerFactoryTest.groovy
index 36e91a4..c87aaec 100644
--- a/subprojects/core/src/test/groovy/org/gradle/api/internal/initialization/DefaultScriptHandlerFactoryTest.groovy
+++ b/subprojects/core/src/test/groovy/org/gradle/api/internal/initialization/DefaultScriptHandlerFactoryTest.groovy
@@ -24,7 +24,7 @@ import org.gradle.api.internal.file.FileResolver
 import org.gradle.groovy.scripts.ScriptSource
 
 import spock.lang.Specification
-import org.gradle.util.MutableURLClassLoader
+import org.gradle.internal.classloader.MutableURLClassLoader
 
 class DefaultScriptHandlerFactoryTest extends Specification {
     private final DependencyMetaDataProvider metaDataProvider = Mock()
diff --git a/subprojects/core/src/test/groovy/org/gradle/api/internal/initialization/DefaultScriptHandlerTest.groovy b/subprojects/core/src/test/groovy/org/gradle/api/internal/initialization/DefaultScriptHandlerTest.groovy
index e295350..5f79e1e 100644
--- a/subprojects/core/src/test/groovy/org/gradle/api/internal/initialization/DefaultScriptHandlerTest.groovy
+++ b/subprojects/core/src/test/groovy/org/gradle/api/internal/initialization/DefaultScriptHandlerTest.groovy
@@ -19,87 +19,76 @@ import org.gradle.api.artifacts.Configuration
 import org.gradle.api.artifacts.ConfigurationContainer
 import org.gradle.api.artifacts.dsl.DependencyHandler
 import org.gradle.api.artifacts.dsl.RepositoryHandler
-import org.gradle.util.JUnit4GroovyMockery
-import org.gradle.util.WrapUtil
-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.groovy.scripts.ScriptSource
-import org.gradle.util.MutableURLClassLoader
+import org.gradle.util.WrapUtil
+import org.gradle.internal.classloader.MutableURLClassLoader
 import org.gradle.util.ConfigureUtil
 
- at RunWith(JMock)
-public class DefaultScriptHandlerTest {
-    private final JUnit4GroovyMockery context = new JUnit4GroovyMockery()
-    private final RepositoryHandler repositoryHandler = context.mock(RepositoryHandler.class)
-    private final DependencyHandler dependencyHandler = context.mock(DependencyHandler.class)
-    private final ConfigurationContainer configurationContainer = context.mock(ConfigurationContainer.class)
-    private final Configuration configuration = context.mock(Configuration.class)
-    private final ScriptSource scriptSource = context.mock(ScriptSource.class)
-    private final MutableURLClassLoader classLoader = context.mock(MutableURLClassLoader.class)
-
-    @Test void addsClasspathConfiguration() {
-        context.checking {
-            one(configurationContainer).add('classpath')
-        }
+import spock.lang.Specification
 
+class DefaultScriptHandlerTest extends Specification {
+    RepositoryHandler repositoryHandler = Mock()
+    DependencyHandler dependencyHandler = Mock()
+    ConfigurationContainer configurationContainer = Mock()
+    Configuration configuration = Mock()
+    ScriptSource scriptSource = Mock()
+    MutableURLClassLoader classLoader = Mock()
+
+    def "adds classpath configuration"() {
+        when:
         new DefaultScriptHandler(scriptSource, repositoryHandler, dependencyHandler, configurationContainer, classLoader)
+
+        then:
+        1 * configurationContainer.create('classpath')
     }
 
-    @Test void createsAClassLoaderAndAddsContentsOfClassPathConfiguration() {
-        DefaultScriptHandler handler = handler()
+    def "creates a class loader and adds contents of classpath configuration"() {
+        def handler = handler()
+        def classLoader = handler.classLoader
 
-        ClassLoader classLoader = handler.classLoader
-        assertThat(classLoader, sameInstance(this.classLoader))
+        expect:
+        classLoader.is this.classLoader
 
-        File file1 = new File('a')
-        File file2 = new File('b')
-        context.checking {
-            one(configuration).getFiles()
-            will(returnValue(WrapUtil.toSet(file1, file2)))
-            one(classLoader).addURL(file1.toURI().toURL())
-            one(classLoader).addURL(file2.toURI().toURL())
-        }
+        def file1 = new File('a')
+        def file2 = new File('b')
 
+        when:
         handler.updateClassPath()
-    }
 
-    @Test void canConfigureRepositories() {
-        DefaultScriptHandler handler = handler()
+        then:
+        1 * configuration.getFiles() >> WrapUtil.toSet(file1, file2)
+        1 * classLoader.addURL(file1.toURI().toURL())
+        1 * classLoader.addURL(file2.toURI().toURL())
+    }
 
+    def "can configure repositories"() {
+        def handler = handler()
         def configure = {
             mavenCentral()
         }
 
-        context.checking {
-            one(repositoryHandler).configure(configure)
-            will { ConfigureUtil.configure(configure, repositoryHandler, false) }
-            one(repositoryHandler).mavenCentral()
-        }
-
+        when:
         handler.repositories(configure)
-    }
 
-    @Test void canConfigureDependencies() {
-        DefaultScriptHandler handler = handler()
+        then:
+        1 * repositoryHandler.configure(configure) >> { ConfigureUtil.configure(configure, repositoryHandler, false) }
+        1 * repositoryHandler.mavenCentral()
+    }
 
-        context.checking {
-            one(dependencyHandler).add('config', 'dep')
-        }
+    def "can configure dependencies"() {
+        def handler = handler()
 
+        when:
         handler.dependencies {
             add('config', 'dep')
         }
+
+        then:
+        1 * dependencyHandler.add('config', 'dep')
     }
 
     private DefaultScriptHandler handler() {
-        context.checking {
-            one(configurationContainer).add('classpath')
-            will(returnValue(configuration))
-        }
+        1 * configurationContainer.create('classpath') >> configuration
         return new DefaultScriptHandler(scriptSource, repositoryHandler, dependencyHandler, configurationContainer, classLoader)
     }
 }
\ No newline at end of file
diff --git a/subprojects/core/src/test/groovy/org/gradle/api/internal/notations/parsers/ClosureToSpecNotationParserTest.groovy b/subprojects/core/src/test/groovy/org/gradle/api/internal/notations/parsers/ClosureToSpecNotationParserTest.groovy
index fbd7659..cf86bae 100644
--- a/subprojects/core/src/test/groovy/org/gradle/api/internal/notations/parsers/ClosureToSpecNotationParserTest.groovy
+++ b/subprojects/core/src/test/groovy/org/gradle/api/internal/notations/parsers/ClosureToSpecNotationParserTest.groovy
@@ -19,9 +19,6 @@ package org.gradle.api.internal.notations.parsers
 import org.gradle.api.internal.notations.api.UnsupportedNotationException
 import spock.lang.Specification
 
-/**
- * by Szczepan Faber, created at: 10/12/12
- */
 class ClosureToSpecNotationParserTest extends Specification {
 
     private ClosureToSpecNotationParser parser = new ClosureToSpecNotationParser()
diff --git a/subprojects/core/src/test/groovy/org/gradle/api/internal/notations/parsers/TimeUnitsParserTest.groovy b/subprojects/core/src/test/groovy/org/gradle/api/internal/notations/parsers/TimeUnitsParserTest.groovy
index 8f70474..a016f9e 100644
--- a/subprojects/core/src/test/groovy/org/gradle/api/internal/notations/parsers/TimeUnitsParserTest.groovy
+++ b/subprojects/core/src/test/groovy/org/gradle/api/internal/notations/parsers/TimeUnitsParserTest.groovy
@@ -22,9 +22,6 @@ import spock.lang.Specification
 import static java.util.concurrent.TimeUnit.MILLISECONDS
 import static java.util.concurrent.TimeUnit.NANOSECONDS
 
-/**
- * by Szczepan Faber, created at: 2/12/13
- */
 class TimeUnitsParserTest extends Specification {
 
     def parser = new TimeUnitsParser()
diff --git a/subprojects/core/src/test/groovy/org/gradle/api/internal/notations/parsers/TypedNotationParserTest.groovy b/subprojects/core/src/test/groovy/org/gradle/api/internal/notations/parsers/TypedNotationParserTest.groovy
index 85881bd..e1a896f 100644
--- a/subprojects/core/src/test/groovy/org/gradle/api/internal/notations/parsers/TypedNotationParserTest.groovy
+++ b/subprojects/core/src/test/groovy/org/gradle/api/internal/notations/parsers/TypedNotationParserTest.groovy
@@ -20,9 +20,6 @@ package org.gradle.api.internal.notations.parsers;
 import org.gradle.api.internal.notations.api.UnsupportedNotationException
 import spock.lang.Specification
 
-/**
- * by Szczepan Faber, created at: 11/10/11
- */
 public class TypedNotationParserTest extends Specification {
 
     def parser = new DummyParser();
diff --git a/subprojects/core/src/test/groovy/org/gradle/api/internal/plugins/DefaultConventionTest.groovy b/subprojects/core/src/test/groovy/org/gradle/api/internal/plugins/DefaultConventionTest.groovy
index 99c255a..87d0f77 100644
--- a/subprojects/core/src/test/groovy/org/gradle/api/internal/plugins/DefaultConventionTest.groovy
+++ b/subprojects/core/src/test/groovy/org/gradle/api/internal/plugins/DefaultConventionTest.groovy
@@ -16,19 +16,17 @@
 
 package org.gradle.api.internal.plugins
 
-import org.gradle.internal.reflect.Instantiator
 import org.gradle.api.internal.ThreadGlobalInstantiator
 import org.gradle.api.plugins.Convention
 import org.gradle.api.plugins.TestPluginConvention1
 import org.gradle.api.plugins.TestPluginConvention2
+import org.gradle.internal.reflect.Instantiator
 import org.junit.Before
 import org.junit.Test
+
 import static org.hamcrest.Matchers.equalTo
 import static org.junit.Assert.*
 
-/**
- * @author Hans Dockter
- */
 class DefaultConventionTest {
     Convention convention
 
diff --git a/subprojects/core/src/test/groovy/org/gradle/api/internal/plugins/DefaultPluginContainerTest.java b/subprojects/core/src/test/groovy/org/gradle/api/internal/plugins/DefaultPluginContainerTest.java
new file mode 100644
index 0000000..5f2e756
--- /dev/null
+++ b/subprojects/core/src/test/groovy/org/gradle/api/internal/plugins/DefaultPluginContainerTest.java
@@ -0,0 +1,128 @@
+/*
+ * Copyright 2013 the original author or authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.gradle.api.internal.plugins;
+
+import org.gradle.api.Plugin;
+import org.gradle.api.internal.project.DefaultProject;
+import org.gradle.api.internal.project.TestPlugin1;
+import org.gradle.api.internal.project.TestPlugin2;
+import org.gradle.api.plugins.UnknownPluginException;
+import org.gradle.util.TestUtil;
+import org.jmock.Expectations;
+import org.jmock.integration.junit4.JUnit4Mockery;
+import org.junit.Before;
+import org.junit.Test;
+
+import static org.hamcrest.Matchers.*;
+import static org.junit.Assert.assertThat;
+
+public class DefaultPluginContainerTest {
+    protected String pluginId = "somePluginId";
+    protected JUnit4Mockery context = new JUnit4Mockery();
+    private final DefaultProject project = TestUtil.createRootProject();
+
+    private PluginRegistry pluginRegistryStub = context.mock(PluginRegistry.class);
+    private DefaultPluginContainer container = new DefaultPluginContainer(pluginRegistryStub, project);
+
+    private TestPlugin1 pluginWithIdMock = new TestPlugin1();
+    private TestPlugin2 pluginWithoutIdMock = new TestPlugin2();
+
+    @Before
+    public void setUp() {
+        context.checking(new Expectations() {{
+            allowing(pluginRegistryStub).getTypeForId(pluginId); will(returnValue(TestPlugin1.class));
+            allowing(pluginRegistryStub).loadPlugin(TestPlugin1.class); will(returnValue(pluginWithIdMock));
+            allowing(pluginRegistryStub).loadPlugin(TestPlugin2.class); will(returnValue(pluginWithoutIdMock));
+        }});
+    }
+
+    @Test
+    public void usePluginById() {
+        Plugin addedPlugin = container.apply(pluginId);
+        assertThat(pluginWithIdMock, sameInstance(addedPlugin));
+        assertThat(container.apply(pluginId), sameInstance(addedPlugin));
+
+        assertThat(container.findPlugin(addedPlugin.getClass()), sameInstance(addedPlugin));
+        assertThat(container.findPlugin(pluginId), sameInstance(addedPlugin));
+    }
+
+    @Test
+    public void usePluginWithIdByType() {
+        Class<? extends Plugin> type = pluginWithIdMock.getClass();
+
+        Plugin addedPlugin = container.apply(type);
+        assertThat(pluginWithIdMock, sameInstance(addedPlugin));
+        assertThat(container.apply(type), sameInstance(addedPlugin));
+        assertThat(container.apply(pluginId), sameInstance(addedPlugin));
+
+        assertThat(container.findPlugin(type), sameInstance(addedPlugin));
+        assertThat(container.findPlugin(pluginId), sameInstance(addedPlugin));
+    }
+
+    @Test
+    public void usePluginWithoutId() {
+        Class<? extends Plugin> type = pluginWithoutIdMock.getClass();
+        Plugin addedPlugin = container.apply(type);
+        assertThat(pluginWithoutIdMock, sameInstance(addedPlugin));
+        assertThat(container.apply(type), sameInstance(addedPlugin));
+
+        assertThat(container.findPlugin(type), sameInstance(addedPlugin));
+    }
+
+    @Test
+    public void hasAndFindForPluginWithId() {
+        container.apply(pluginId);
+        assertThat(container.hasPlugin(pluginId), equalTo(true));
+        assertThat(container.hasPlugin(pluginWithIdMock.getClass()), equalTo(true));
+        assertThat(container.findPlugin(pluginId), sameInstance((Plugin) pluginWithIdMock));
+        assertThat(container.findPlugin(pluginWithIdMock.getClass()), sameInstance((Plugin) pluginWithIdMock));
+    }
+
+    @Test
+    public void hasAndFindForUnknownPluginId() {
+        context.checking(new Expectations() {{
+            allowing(pluginRegistryStub).getTypeForId("unknown"); will(throwException(new UnknownPluginException("unknown")));
+        }});
+
+        assertThat(container.hasPlugin("unknown"), equalTo(false));
+        assertThat(container.findPlugin("unknown"), nullValue());
+    }
+
+    @Test
+    public void hasAndFindForPluginWithoutId() {
+        Plugin plugin = pluginWithoutIdMock;
+        Class<? extends Plugin> pluginType = plugin.getClass();
+        container.apply(pluginType);
+        assertThat(container.hasPlugin(pluginType), equalTo(true));
+        assertThat(container.findPlugin(pluginType), sameInstance(plugin));
+    }
+
+    @Test
+    public void hasAndFindPluginByTypeWithUnknownPlugin() {
+        assertThat(container.hasPlugin(TestPlugin2.class), equalTo(false));
+        assertThat(container.findPlugin(TestPlugin2.class), nullValue());
+    }
+
+    @Test(expected = UnknownPluginException.class)
+    public void getNonUsedPluginById() {
+        container.getPlugin(pluginId);
+    }
+
+    @Test(expected = UnknownPluginException.class)
+    public void getNonUsedPluginByType() {
+        container.getPlugin(TestPlugin1.class);
+    }
+}
diff --git a/subprojects/core/src/test/groovy/org/gradle/api/internal/plugins/DefaultProjectsPluginContainerTest.java b/subprojects/core/src/test/groovy/org/gradle/api/internal/plugins/DefaultProjectsPluginContainerTest.java
deleted file mode 100644
index 5c27473..0000000
--- a/subprojects/core/src/test/groovy/org/gradle/api/internal/plugins/DefaultProjectsPluginContainerTest.java
+++ /dev/null
@@ -1,131 +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.gradle.api.Plugin;
-import org.gradle.api.internal.project.DefaultProject;
-import org.gradle.api.internal.project.TestPlugin1;
-import org.gradle.api.internal.project.TestPlugin2;
-import org.gradle.api.plugins.UnknownPluginException;
-import org.gradle.util.HelperUtil;
-import org.jmock.Expectations;
-import org.jmock.integration.junit4.JUnit4Mockery;
-import org.junit.Before;
-import org.junit.Test;
-
-import static org.hamcrest.Matchers.*;
-import static org.junit.Assert.assertThat;
-
-/**
- * @author Hans Dockter
- */
-public class DefaultProjectsPluginContainerTest {
-    protected String pluginId = "somePluginId";
-    protected JUnit4Mockery context = new JUnit4Mockery();
-    private final DefaultProject project = HelperUtil.createRootProject();
-
-    private PluginRegistry pluginRegistryStub = context.mock(PluginRegistry.class);
-    private DefaultProjectsPluginContainer container = new DefaultProjectsPluginContainer(pluginRegistryStub, project);
-
-    private TestPlugin1 pluginWithIdMock = new TestPlugin1();
-    private TestPlugin2 pluginWithoutIdMock = new TestPlugin2();
-
-    @Before
-    public void setUp() {
-        context.checking(new Expectations() {{
-            allowing(pluginRegistryStub).getTypeForId(pluginId); will(returnValue(TestPlugin1.class));
-            allowing(pluginRegistryStub).loadPlugin(TestPlugin1.class); will(returnValue(pluginWithIdMock));
-            allowing(pluginRegistryStub).loadPlugin(TestPlugin2.class); will(returnValue(pluginWithoutIdMock));
-        }});
-    }
-
-    @Test
-    public void usePluginById() {
-        Plugin addedPlugin = container.apply(pluginId);
-        assertThat(pluginWithIdMock, sameInstance(addedPlugin));
-        assertThat(container.apply(pluginId), sameInstance(addedPlugin));
-
-        assertThat(container.findPlugin(addedPlugin.getClass()), sameInstance(addedPlugin));
-        assertThat(container.findPlugin(pluginId), sameInstance(addedPlugin));
-    }
-
-    @Test
-    public void usePluginWithIdByType() {
-        Class<? extends Plugin> type = pluginWithIdMock.getClass();
-
-        Plugin addedPlugin = container.apply(type);
-        assertThat(pluginWithIdMock, sameInstance(addedPlugin));
-        assertThat(container.apply(type), sameInstance(addedPlugin));
-        assertThat(container.apply(pluginId), sameInstance(addedPlugin));
-
-        assertThat(container.findPlugin(type), sameInstance(addedPlugin));
-        assertThat(container.findPlugin(pluginId), sameInstance(addedPlugin));
-    }
-
-    @Test
-    public void usePluginWithoutId() {
-        Class<? extends Plugin> type = pluginWithoutIdMock.getClass();
-        Plugin addedPlugin = container.apply(type);
-        assertThat(pluginWithoutIdMock, sameInstance(addedPlugin));
-        assertThat(container.apply(type), sameInstance(addedPlugin));
-
-        assertThat(container.findPlugin(type), sameInstance(addedPlugin));
-    }
-
-    @Test
-    public void hasAndFindForPluginWithId() {
-        container.apply(pluginId);
-        assertThat(container.hasPlugin(pluginId), equalTo(true));
-        assertThat(container.hasPlugin(pluginWithIdMock.getClass()), equalTo(true));
-        assertThat(container.findPlugin(pluginId), sameInstance((Plugin) pluginWithIdMock));
-        assertThat(container.findPlugin(pluginWithIdMock.getClass()), sameInstance((Plugin) pluginWithIdMock));
-    }
-
-    @Test
-    public void hasAndFindForUnknownPluginId() {
-        context.checking(new Expectations() {{
-            allowing(pluginRegistryStub).getTypeForId("unknown"); will(throwException(new UnknownPluginException("unknown")));
-        }});
-
-        assertThat(container.hasPlugin("unknown"), equalTo(false));
-        assertThat(container.findPlugin("unknown"), nullValue());
-    }
-
-    @Test
-    public void hasAndFindForPluginWithoutId() {
-        Plugin plugin = pluginWithoutIdMock;
-        Class<? extends Plugin> pluginType = plugin.getClass();
-        container.apply(pluginType);
-        assertThat(container.hasPlugin(pluginType), equalTo(true));
-        assertThat(container.findPlugin(pluginType), sameInstance(plugin));
-    }
-
-    @Test
-    public void hasAndFindPluginByTypeWithUnknownPlugin() {
-        assertThat(container.hasPlugin(TestPlugin2.class), equalTo(false));
-        assertThat(container.findPlugin(TestPlugin2.class), nullValue());
-    }
-
-    @Test(expected = UnknownPluginException.class)
-    public void getNonUsedPluginById() {
-        container.getPlugin(pluginId);
-    }
-
-    @Test(expected = UnknownPluginException.class)
-    public void getNonUsedPluginByType() {
-        container.getPlugin(TestPlugin1.class);
-    }
-}
diff --git a/subprojects/core/src/test/groovy/org/gradle/api/internal/plugins/ExtensionContainerTest.groovy b/subprojects/core/src/test/groovy/org/gradle/api/internal/plugins/ExtensionContainerTest.groovy
index 2c78e79..8a4a1b0 100644
--- a/subprojects/core/src/test/groovy/org/gradle/api/internal/plugins/ExtensionContainerTest.groovy
+++ b/subprojects/core/src/test/groovy/org/gradle/api/internal/plugins/ExtensionContainerTest.groovy
@@ -23,9 +23,6 @@ import org.gradle.api.plugins.ExtensionAware
 import org.gradle.api.plugins.ExtraPropertiesExtension
 import spock.lang.Specification
 
-/**
- * @author: Szczepan Faber, created at: 6/24/11
- */
 public class ExtensionContainerTest extends Specification {
 
     def container = new DefaultConvention(ThreadGlobalInstantiator.getOrCreate())
diff --git a/subprojects/core/src/test/groovy/org/gradle/api/internal/plugins/ExtensionsStorageTest.groovy b/subprojects/core/src/test/groovy/org/gradle/api/internal/plugins/ExtensionsStorageTest.groovy
index 9e7049f..9056abc 100644
--- a/subprojects/core/src/test/groovy/org/gradle/api/internal/plugins/ExtensionsStorageTest.groovy
+++ b/subprojects/core/src/test/groovy/org/gradle/api/internal/plugins/ExtensionsStorageTest.groovy
@@ -17,6 +17,7 @@
 package org.gradle.api.internal.plugins
 
 import org.gradle.api.Action
+import org.gradle.api.InvalidUserDataException
 import org.gradle.api.UnknownDomainObjectException
 import org.gradle.api.plugins.DeferredConfigurable
 import spock.lang.Specification
@@ -132,6 +133,81 @@ class ExtensionsStorageTest extends Specification {
         1 * delegate.call(2)
     }
 
+    def "propagates configure exception on each attempt to access deferred configurable exception"() {
+
+        TestDeferredExtension extension = new TestDeferredExtension()
+        def delegate = Mock(TestExtension)
+        extension.delegate = delegate
+
+        given:
+        storage.add("ext", extension)
+        storage.configureExtension("ext", {
+            throw new RuntimeException("bad")
+        })
+
+        when:
+        storage.getByName("ext")
+
+        then:
+        def first = thrown RuntimeException
+        first.message == "bad"
+
+        when:
+        storage.getByName("ext")
+
+        then:
+        def second = thrown RuntimeException
+        second == first
+    }
+
+    def "rethrows unknown domain object exception thrown by deferred configurable extension config"() {
+
+        TestDeferredExtension extension = new TestDeferredExtension()
+        def delegate = Mock(TestExtension)
+        extension.delegate = delegate
+
+        when:
+        storage.add("ext", extension)
+        storage.configureExtension("ext", {
+            throw new UnknownDomainObjectException("ORIGINAL")
+        })
+
+        then:
+        0 * _
+
+        when:
+        storage.findByType(TestDeferredExtension)
+
+        then:
+        def t = thrown UnknownDomainObjectException
+        t.message == "ORIGINAL"
+    }
+
+    def "cannot configure deferred configurable extension after access"() {
+
+        TestDeferredExtension extension = new TestDeferredExtension()
+        def delegate = Mock(TestExtension)
+        extension.delegate = delegate
+
+        given:
+        storage.add("ext", extension)
+        storage.configureExtension("ext", {
+            it.call(1)
+        })
+
+        and:
+        storage.getByName("ext")
+
+        when:
+        storage.configureExtension("ext", {
+            it.call(2)
+        })
+
+        then:
+        def t = thrown InvalidUserDataException
+        t.message == "Cannot configure the 'ext' extension after it has been accessed."
+    }
+
     public static interface TestExtension {
         void call(def value);
     }
diff --git a/subprojects/core/src/test/groovy/org/gradle/api/internal/project/DefaultAntBuilderFactoryTest.groovy b/subprojects/core/src/test/groovy/org/gradle/api/internal/project/DefaultAntBuilderFactoryTest.groovy
index 87a851b..b6e240a 100644
--- a/subprojects/core/src/test/groovy/org/gradle/api/internal/project/DefaultAntBuilderFactoryTest.groovy
+++ b/subprojects/core/src/test/groovy/org/gradle/api/internal/project/DefaultAntBuilderFactoryTest.groovy
@@ -17,7 +17,7 @@ package org.gradle.api.internal.project
 
 import org.apache.tools.ant.BuildListener
 import org.gradle.api.Project
-import org.gradle.util.HelperUtil
+import org.gradle.util.TestUtil
 import org.gradle.util.JUnit4GroovyMockery
 import org.jmock.integration.junit4.JMock
 import org.junit.Before
@@ -30,7 +30,7 @@ import static org.junit.Assert.*
 public class DefaultAntBuilderFactoryTest {
     private final JUnit4GroovyMockery context = new JUnit4GroovyMockery()
     private final BuildListener listener = context.mock(BuildListener)
-    private final Project project = HelperUtil.createRootProject()
+    private final Project project = TestUtil.createRootProject()
     private final DefaultAntBuilderFactory factory = new DefaultAntBuilderFactory(listener, project)
 
     @Before
diff --git a/subprojects/core/src/test/groovy/org/gradle/api/internal/project/DefaultAntBuilderTest.groovy b/subprojects/core/src/test/groovy/org/gradle/api/internal/project/DefaultAntBuilderTest.groovy
index f3c5ad4..37025bc 100644
--- a/subprojects/core/src/test/groovy/org/gradle/api/internal/project/DefaultAntBuilderTest.groovy
+++ b/subprojects/core/src/test/groovy/org/gradle/api/internal/project/DefaultAntBuilderTest.groovy
@@ -18,7 +18,7 @@ package org.gradle.api.internal.project
 import groovy.xml.MarkupBuilder
 import org.junit.Test
 import org.gradle.api.Project
-import org.gradle.util.HelperUtil
+import org.gradle.util.TestUtil
 import static org.junit.Assert.*
 import static org.hamcrest.Matchers.*
 import static org.gradle.util.Matchers.*
@@ -28,7 +28,7 @@ import org.apache.tools.ant.Target
 import org.apache.tools.ant.Task
 
 class DefaultAntBuilderTest {
-    private final Project project = HelperUtil.createRootProject()
+    private final Project project = TestUtil.createRootProject()
     private final def ant = new DefaultAntBuilder(project)
 
     @Test
diff --git a/subprojects/core/src/test/groovy/org/gradle/api/internal/project/DefaultIsolatedAntBuilderTest.groovy b/subprojects/core/src/test/groovy/org/gradle/api/internal/project/DefaultIsolatedAntBuilderTest.groovy
index ebf5826..c4082c8 100644
--- a/subprojects/core/src/test/groovy/org/gradle/api/internal/project/DefaultIsolatedAntBuilderTest.groovy
+++ b/subprojects/core/src/test/groovy/org/gradle/api/internal/project/DefaultIsolatedAntBuilderTest.groovy
@@ -28,7 +28,7 @@ import org.gradle.api.internal.classpath.ModuleRegistry
 import org.gradle.api.internal.project.ant.BasicAntBuilder
 import org.gradle.logging.ConfigureLogging
 import org.gradle.logging.TestAppender
-import org.gradle.util.DefaultClassLoaderFactory
+import org.gradle.internal.classloader.DefaultClassLoaderFactory
 import org.junit.Before
 import org.junit.Rule
 import org.junit.Test
@@ -36,7 +36,7 @@ import static org.hamcrest.Matchers.*
 import static org.junit.Assert.assertThat
 import static org.junit.Assert.fail
 import org.apache.tools.ant.Task
-import org.gradle.util.ClasspathUtil
+import org.gradle.internal.classloader.ClasspathUtil
 
 class DefaultIsolatedAntBuilderTest {
     private final ModuleRegistry moduleRegistry = new DefaultModuleRegistry()
diff --git a/subprojects/core/src/test/groovy/org/gradle/api/internal/project/DefaultProjectRegistryTest.java b/subprojects/core/src/test/groovy/org/gradle/api/internal/project/DefaultProjectRegistryTest.java
index 7b9c0d1..24bb1c7 100644
--- a/subprojects/core/src/test/groovy/org/gradle/api/internal/project/DefaultProjectRegistryTest.java
+++ b/subprojects/core/src/test/groovy/org/gradle/api/internal/project/DefaultProjectRegistryTest.java
@@ -18,7 +18,7 @@ package org.gradle.api.internal.project;
 import org.gradle.api.InvalidUserDataException;
 import org.gradle.api.Project;
 import org.gradle.api.specs.Spec;
-import org.gradle.util.HelperUtil;
+import org.gradle.util.TestUtil;
 import org.junit.Before;
 import org.junit.Test;
 
@@ -26,18 +26,11 @@ import java.util.SortedSet;
 import java.util.TreeSet;
 
 import static junit.framework.Assert.assertSame;
-import static org.gradle.util.WrapUtil.*;
+import static org.gradle.util.WrapUtil.toSet;
+import static org.gradle.util.WrapUtil.toSortedSet;
 import static org.hamcrest.Matchers.*;
-import static org.junit.Assert.assertEquals;
-import static org.junit.Assert.assertFalse;
-import static org.junit.Assert.assertNull;
 import static org.junit.Assert.*;
-import static org.junit.Assert.assertTrue;
-import static org.junit.Assert.fail;
 
-/**
- * @author Hans Dockter
- */
 public class DefaultProjectRegistryTest {
     public static final String CHILD_NAME = "child";
     public static final String CHILD_CHILD_NAME = "childchild";
@@ -50,9 +43,9 @@ public class DefaultProjectRegistryTest {
     @Before
     public void setUp() {
         projectRegistry = new DefaultProjectRegistry<ProjectInternal>();
-        rootMock = HelperUtil.createRootProject();
-        childMock = HelperUtil.createChildProject(rootMock, CHILD_NAME);
-        childChildMock = HelperUtil.createChildProject(childMock, CHILD_CHILD_NAME);
+        rootMock = TestUtil.createRootProject();
+        childMock = TestUtil.createChildProject(rootMock, CHILD_NAME);
+        childChildMock = TestUtil.createChildProject(childMock, CHILD_CHILD_NAME);
         projectRegistry.addProject(rootMock);
         projectRegistry.addProject(childMock);
         projectRegistry.addProject(childChildMock);
@@ -77,7 +70,7 @@ public class DefaultProjectRegistryTest {
 
     @Test
     public void cannotLocateProjectsWithAmbiguousProjectDir() {
-        DefaultProject duplicateProjectDirProject = HelperUtil.createChildProject(childMock, "childchild2", childMock.getProjectDir());
+        DefaultProject duplicateProjectDirProject = TestUtil.createChildProject(childMock, "childchild2", childMock.getProjectDir());
         projectRegistry.addProject(duplicateProjectDirProject);
 
         try {
@@ -91,7 +84,7 @@ public class DefaultProjectRegistryTest {
     @Test
     public void accessMethodsForNonExistingsPaths() {
         projectRegistry = new DefaultProjectRegistry<ProjectInternal>();
-        Project otherRoot = HelperUtil.createRootProject();
+        Project otherRoot = TestUtil.createRootProject();
         assertNull(projectRegistry.getProject(otherRoot.getPath()));
         assertEquals(new TreeSet<ProjectInternal>(), projectRegistry.getAllProjects(otherRoot.getPath()));
         assertEquals(new TreeSet<ProjectInternal>(), projectRegistry.getSubProjects(otherRoot.getPath()));
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 32c4cee..9bab2e2 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
@@ -17,15 +17,17 @@
 package org.gradle.api.internal.project
 
 import org.apache.tools.ant.types.FileSet
+import org.gradle.api.*
 import org.gradle.api.artifacts.Module
 import org.gradle.api.artifacts.dsl.ArtifactHandler
 import org.gradle.api.artifacts.dsl.DependencyHandler
+import org.gradle.api.artifacts.dsl.ComponentMetadataHandler
 import org.gradle.api.artifacts.dsl.RepositoryHandler
 import org.gradle.api.component.SoftwareComponentContainer
 import org.gradle.api.initialization.dsl.ScriptHandler
+import org.gradle.api.internal.*
 import org.gradle.api.internal.artifacts.configurations.ConfigurationContainerInternal
 import org.gradle.api.internal.artifacts.configurations.DependencyMetaDataProvider
-import org.gradle.api.internal.artifacts.dsl.dependencies.DependencyFactory
 import org.gradle.api.internal.file.FileOperations
 import org.gradle.api.internal.file.FileResolver
 import org.gradle.api.internal.initialization.ScriptClassLoaderProvider
@@ -33,52 +35,44 @@ import org.gradle.api.internal.tasks.TaskContainerInternal
 import org.gradle.api.invocation.Gradle
 import org.gradle.api.plugins.PluginContainer
 import org.gradle.api.tasks.Directory
-import org.gradle.configuration.ProjectEvaluator
 import org.gradle.configuration.ScriptPluginFactory
+import org.gradle.configuration.project.ProjectConfigurationActionContainer
+import org.gradle.configuration.project.ProjectEvaluator
 import org.gradle.groovy.scripts.EmptyScript
 import org.gradle.groovy.scripts.ScriptSource
 import org.gradle.internal.Factory
 import org.gradle.internal.reflect.Instantiator
 import org.gradle.internal.service.ServiceRegistry
+import org.gradle.internal.service.scopes.ServiceRegistryFactory
 import org.gradle.logging.LoggingManagerInternal
 import org.gradle.logging.StandardOutputCapture
-import org.gradle.util.HelperUtil
 import org.gradle.util.JUnit4GroovyMockery
 import org.gradle.util.TestClosure
+import org.gradle.util.TestUtil
 import org.jmock.integration.junit4.JMock
 import org.junit.Before
 import org.junit.Test
 import org.junit.runner.RunWith
 
-import java.awt.Point
+import java.awt.*
 import java.text.FieldPosition
 
-import org.gradle.api.*
-import org.gradle.api.internal.*
-
 import static org.hamcrest.Matchers.*
 import static org.junit.Assert.*
 
-/**
- * @author Hans Dockter
- */
 @RunWith (JMock.class)
 class DefaultProjectTest {
     JUnit4GroovyMockery context = new JUnit4GroovyMockery()
 
-    static final String TEST_PROJECT_NAME = 'testproject'
-
     static final String TEST_BUILD_FILE_NAME = 'build.gradle'
 
-    static final String TEST_TASK_NAME = 'testtask'
-
     Task testTask;
 
     DefaultProject project, child1, child2, childchild
 
     ProjectEvaluator projectEvaluator = context.mock(ProjectEvaluator.class)
 
-    IProjectRegistry projectRegistry
+    ProjectRegistry projectRegistry
 
     File rootDir
 
@@ -94,8 +88,8 @@ class DefaultProjectTest {
 
     ConfigurationContainerInternal configurationContainerMock = context.mock(ConfigurationContainerInternal.class)
     RepositoryHandler repositoryHandlerMock = context.mock(RepositoryHandler.class)
-    DependencyFactory dependencyFactoryMock = context.mock(DependencyFactory.class)
     DependencyHandler dependencyHandlerMock = context.mock(DependencyHandler)
+    ComponentMetadataHandler moduleHandlerMock = context.mock(ComponentMetadataHandler)
     PluginContainer pluginContainerMock = context.mock(PluginContainer)
     ScriptHandler scriptHandlerMock = context.mock(ScriptHandler)
     DependencyMetaDataProvider dependencyMetaDataProviderMock = context.mock(DependencyMetaDataProvider)
@@ -105,6 +99,7 @@ class DefaultProjectTest {
     LoggingManagerInternal loggingManagerMock = context.mock(LoggingManagerInternal.class)
     Instantiator instantiatorMock = context.mock(Instantiator)
     SoftwareComponentContainer softwareComponentsMock = context.mock(SoftwareComponentContainer.class)
+    ProjectConfigurationActionContainer configureActions = context.mock(ProjectConfigurationActionContainer.class)
 
     @Before
     void setUp() {
@@ -120,7 +115,7 @@ class DefaultProjectTest {
 
         testScript = new EmptyScript()
 
-        testTask = HelperUtil.createTask(DefaultTask)
+        testTask = TestUtil.createTask(DefaultTask)
 
         projectRegistry = new DefaultProjectRegistry()
 
@@ -135,6 +130,7 @@ class DefaultProjectTest {
             allowing(serviceRegistryMock).get(ConfigurationContainerInternal); will(returnValue(configurationContainerMock))
             allowing(serviceRegistryMock).get(ArtifactHandler); will(returnValue(context.mock(ArtifactHandler)))
             allowing(serviceRegistryMock).get(DependencyHandler); will(returnValue(dependencyHandlerMock))
+            allowing(serviceRegistryMock).get(ComponentMetadataHandler); will(returnValue(moduleHandlerMock))
             allowing(serviceRegistryMock).get(SoftwareComponentContainer); will(returnValue(softwareComponentsMock))
             allowing(serviceRegistryMock).get(ProjectEvaluator); will(returnValue(projectEvaluator))
             allowing(serviceRegistryMock).getFactory(AntBuilder); will(returnValue(antBuilderFactoryMock))
@@ -143,13 +139,14 @@ class DefaultProjectTest {
             allowing(serviceRegistryMock).get(ScriptClassLoaderProvider); will(returnValue(context.mock(ScriptClassLoaderProvider)))
             allowing(serviceRegistryMock).get(LoggingManagerInternal); will(returnValue(loggingManagerMock))
             allowing(serviceRegistryMock).get(StandardOutputCapture); will(returnValue(context.mock(StandardOutputCapture)))
-            allowing(serviceRegistryMock).get(IProjectRegistry); will(returnValue(projectRegistry))
+            allowing(serviceRegistryMock).get(ProjectRegistry); will(returnValue(projectRegistry))
             allowing(serviceRegistryMock).get(DependencyMetaDataProvider); will(returnValue(dependencyMetaDataProviderMock))
             allowing(serviceRegistryMock).get(FileResolver); will(returnValue([toString: { -> "file resolver" }] as FileResolver))
             allowing(serviceRegistryMock).get(Instantiator); will(returnValue(instantiatorMock))
             allowing(serviceRegistryMock).get(FileOperations); will(returnValue(fileOperationsMock))
             allowing(serviceRegistryMock).get(ProcessOperations); will(returnValue(processOperationsMock))
             allowing(serviceRegistryMock).get(ScriptPluginFactory); will(returnValue([toString: { -> "script plugin factory" }] as ScriptPluginFactory))
+            allowing(serviceRegistryMock).get(ProjectConfigurationActionContainer); will(returnValue(configureActions))
             Object listener = context.mock(ProjectEvaluationListener)
             ignoring(listener)
             allowing(build).getProjectEvaluationBroadcaster();
@@ -255,7 +252,7 @@ class DefaultProjectTest {
             one(listener).call(project)
         }
 
-        project.beforeEvaluate(HelperUtil.toClosure(listener))
+        project.beforeEvaluate(TestUtil.toClosure(listener))
         project.projectEvaluationBroadcaster.beforeEvaluate(project)
     }
 
@@ -265,7 +262,7 @@ class DefaultProjectTest {
             one(listener).call(project)
         }
 
-        project.afterEvaluate(HelperUtil.toClosure(listener))
+        project.afterEvaluate(TestUtil.toClosure(listener))
         project.projectEvaluationBroadcaster.afterEvaluate(project, null)
     }
 
@@ -551,9 +548,9 @@ class DefaultProjectTest {
     }
 
     @Test void testGetAllTasksRecursive() {
-        Task projectTask = HelperUtil.createTask(DefaultTask.class)
-        Task child1Task = HelperUtil.createTask(DefaultTask.class)
-        Task child2Task = HelperUtil.createTask(DefaultTask.class)
+        Task projectTask = TestUtil.createTask(DefaultTask.class)
+        Task child1Task = TestUtil.createTask(DefaultTask.class)
+        Task child2Task = TestUtil.createTask(DefaultTask.class)
 
         Map expectedMap = new TreeMap()
         expectedMap[project] = [projectTask] as TreeSet
@@ -576,7 +573,7 @@ class DefaultProjectTest {
     }
 
     @Test void testGetAllTasksNonRecursive() {
-        Task projectTask = HelperUtil.createTask(DefaultTask.class)
+        Task projectTask = TestUtil.createTask(DefaultTask.class)
 
         Map expectedMap = new TreeMap()
         expectedMap[project] = [projectTask] as TreeSet
@@ -590,8 +587,8 @@ class DefaultProjectTest {
     }
 
     @Test void testGetTasksByNameRecursive() {
-        Task projectTask = HelperUtil.createTask(DefaultTask.class)
-        Task child1Task = HelperUtil.createTask(DefaultTask.class)
+        Task projectTask = TestUtil.createTask(DefaultTask.class)
+        Task child1Task = TestUtil.createTask(DefaultTask.class)
 
         context.checking {
             one(taskContainerMock).findByName('task'); will(returnValue(projectTask))
@@ -604,7 +601,7 @@ class DefaultProjectTest {
     }
 
     @Test void testGetTasksByNameNonRecursive() {
-        Task projectTask = HelperUtil.createTask(DefaultTask.class)
+        Task projectTask = TestUtil.createTask(DefaultTask.class)
 
         context.checking {
             one(taskContainerMock).findByName('task'); will(returnValue(projectTask))
@@ -661,7 +658,7 @@ def scriptMethod(Closure closure) {
     "$returnValue"
 }
 """
-        HelperUtil.createScript(code)
+        TestUtil.createScript(code)
     }
 
     @Test void testSetPropertyAndPropertyMissingWithProjectProperty() {
@@ -789,44 +786,44 @@ def scriptMethod(Closure closure) {
     }
 
     @Test public void testDir() {
-        Task dirTask1 = HelperUtil.createTask(Directory.class)
-        Task dirTask12 = HelperUtil.createTask(Directory.class)
-        Task dirTask123 = HelperUtil.createTask(Directory.class)
+        Task dirTask1 = TestUtil.createTask(Directory.class)
+        Task dirTask12 = TestUtil.createTask(Directory.class)
+        Task dirTask123 = TestUtil.createTask(Directory.class)
         context.checking {
             one(taskContainerMock).findByName('dir1'); will(returnValue(null))
-            one(taskContainerMock).add('dir1', Directory); will(returnValue(dirTask1))
+            one(taskContainerMock).create('dir1', Directory); will(returnValue(dirTask1))
             one(taskContainerMock).findByName('dir1/dir2'); will(returnValue(null))
-            one(taskContainerMock).add('dir1/dir2', Directory); will(returnValue(dirTask12))
+            one(taskContainerMock).create('dir1/dir2', Directory); will(returnValue(dirTask12))
             one(taskContainerMock).findByName('dir1/dir2/dir3'); will(returnValue(null))
-            one(taskContainerMock).add('dir1/dir2/dir3', Directory); will(returnValue(dirTask123))
+            one(taskContainerMock).create('dir1/dir2/dir3', Directory); will(returnValue(dirTask123))
         }
         assertSame(dirTask123, project.dir('dir1/dir2/dir3'));
     }
 
     @Test public void testDirWithExistingParentDirTask() {
-        Task dirTask1 = HelperUtil.createTask(Directory.class)
+        Task dirTask1 = TestUtil.createTask(Directory.class)
         context.checking {
             one(taskContainerMock).findByName('dir1'); will(returnValue(null))
-            one(taskContainerMock).add('dir1', Directory); will(returnValue(dirTask1))
+            one(taskContainerMock).create('dir1', Directory); will(returnValue(dirTask1))
         }
         project.dir('dir1')
 
-        Task dirTask14 = HelperUtil.createTask(Directory.class)
+        Task dirTask14 = TestUtil.createTask(Directory.class)
         context.checking {
             one(taskContainerMock).findByName('dir1'); will(returnValue(dirTask1))
             one(taskContainerMock).findByName('dir1/dir4'); will(returnValue(null))
-            one(taskContainerMock).add('dir1/dir4', Directory); will(returnValue(dirTask14))
+            one(taskContainerMock).create('dir1/dir4', Directory); will(returnValue(dirTask14))
         }
         assertSame(dirTask14, project.dir('dir1/dir4'))
     }
 
     @Test public void testDirWithConflictingNonDirTask() {
-        Task dirTask14 = HelperUtil.createTask(DefaultTask.class)
+        Task dirTask14 = TestUtil.createTask(DefaultTask.class)
 
-        Task dirTask1 = HelperUtil.createTask(Directory.class)
+        Task dirTask1 = TestUtil.createTask(Directory.class)
         context.checking {
             one(taskContainerMock).findByName('dir1'); will(returnValue(null))
-            one(taskContainerMock).add('dir1', Directory); will(returnValue(dirTask1))
+            one(taskContainerMock).create('dir1', Directory); will(returnValue(dirTask1))
             one(taskContainerMock).findByName('dir1/dir4'); will(returnValue(dirTask14))
         }
 
@@ -894,7 +891,7 @@ def scriptMethod(Closure closure) {
     private void checkConfigureProject(String configureMethod, Set projectsToCheck) {
         String propValue = 'someValue'
         if (configureMethod == 'configure') {
-            project."$configureMethod" projectsToCheck as List,
+            project."$configureMethod" projectsToCheck as java.util.List,
                     {
                         testSubProp = propValue
                     }
diff --git a/subprojects/core/src/test/groovy/org/gradle/api/internal/project/GlobalServicesRegistryTest.java b/subprojects/core/src/test/groovy/org/gradle/api/internal/project/GlobalServicesRegistryTest.java
deleted file mode 100755
index 664b628..0000000
--- a/subprojects/core/src/test/groovy/org/gradle/api/internal/project/GlobalServicesRegistryTest.java
+++ /dev/null
@@ -1,142 +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;
-
-import org.gradle.api.internal.*;
-import org.gradle.api.internal.classpath.DefaultModuleRegistry;
-import org.gradle.api.internal.classpath.DefaultPluginModuleRegistry;
-import org.gradle.api.internal.classpath.ModuleRegistry;
-import org.gradle.api.internal.classpath.PluginModuleRegistry;
-import org.gradle.cache.internal.CacheFactory;
-import org.gradle.cache.internal.DefaultCacheFactory;
-import org.gradle.cache.internal.DefaultFileLockManager;
-import org.gradle.cache.internal.FileLockManager;
-import org.gradle.initialization.ClassLoaderRegistry;
-import org.gradle.cli.CommandLineConverter;
-import org.gradle.initialization.DefaultClassLoaderRegistry;
-import org.gradle.initialization.DefaultCommandLineConverter;
-import org.gradle.internal.nativeplatform.*;
-import org.gradle.internal.nativeplatform.filesystem.FileSystem;
-import org.gradle.listener.DefaultListenerManager;
-import org.gradle.listener.ListenerManager;
-import org.gradle.logging.LoggingManagerInternal;
-import org.gradle.logging.ProgressLoggerFactory;
-import org.gradle.logging.internal.DefaultLoggingManagerFactory;
-import org.gradle.logging.internal.DefaultProgressLoggerFactory;
-import org.gradle.messaging.remote.MessagingServer;
-import org.gradle.util.ClassLoaderFactory;
-import org.gradle.util.DefaultClassLoaderFactory;
-import org.junit.Test;
-
-import static org.hamcrest.Matchers.instanceOf;
-import static org.hamcrest.Matchers.notNullValue;
-import static org.junit.Assert.assertThat;
-
-public class GlobalServicesRegistryTest {
-    private final GlobalServicesRegistry registry = new GlobalServicesRegistry();
-
-    @Test
-    public void providesCommandLineArgsConverter() {
-        assertThat(registry.get(CommandLineConverter.class), instanceOf(
-                DefaultCommandLineConverter.class));
-    }
-
-    @Test
-    public void providesACacheFactoryFactory() {
-        assertThat(registry.getFactory(CacheFactory.class), instanceOf(DefaultCacheFactory.class));
-    }
-
-    @Test
-    public void providesAModuleRegistry() {
-        assertThat(registry.get(ModuleRegistry.class), instanceOf(DefaultModuleRegistry.class));
-    }
-
-    @Test
-    public void providesAPluginModuleRegistry() {
-        assertThat(registry.get(PluginModuleRegistry.class), instanceOf(DefaultPluginModuleRegistry.class));
-    }
-
-    @Test
-    public void providesAClassPathRegistry() {
-        assertThat(registry.get(ClassPathRegistry.class), instanceOf(DefaultClassPathRegistry.class));
-    }
-
-    @Test
-    public void providesAClassLoaderRegistry() {
-        assertThat(registry.get(ClassLoaderRegistry.class), instanceOf(DefaultClassLoaderRegistry.class));
-    }
-
-    @Test
-    public void providesALoggingManagerFactory() {
-        assertThat(registry.getFactory(LoggingManagerInternal.class), instanceOf(DefaultLoggingManagerFactory.class));
-    }
-    
-    @Test
-    public void providesAListenerManager() {
-        assertThat(registry.get(ListenerManager.class), instanceOf(DefaultListenerManager.class));
-    }
-    
-    @Test
-    public void providesAProgressLoggerFactory() {
-        assertThat(registry.get(ProgressLoggerFactory.class), instanceOf(DefaultProgressLoggerFactory.class));
-    }
-    
-    @Test
-    public void providesAGradleDistributionLocator() {
-        assertThat(registry.get(GradleDistributionLocator.class), instanceOf(DefaultModuleRegistry.class));
-    }
-    
-    @Test
-    public void providesAClassLoaderFactory() {
-        assertThat(registry.get(ClassLoaderFactory.class), instanceOf(DefaultClassLoaderFactory.class));
-    }
-
-    @Test
-    public void providesAMessagingServer() {
-        assertThat(registry.get(MessagingServer.class), instanceOf(MessagingServer.class));
-    }
-
-    @Test
-    public void providesAClassGenerator() {
-        assertThat(registry.get(ClassGenerator.class), instanceOf(AsmBackedClassGenerator.class));
-    }
-    
-    @Test
-    public void providesAnInstantiator() {
-        assertThat(registry.get(org.gradle.internal.reflect.Instantiator.class), instanceOf(ClassGeneratorBackedInstantiator.class));
-    }
-
-    @Test
-    public void providesAFileLockManager() {
-        assertThat(registry.get(FileLockManager.class), instanceOf(DefaultFileLockManager.class));
-    }
-
-    @Test
-    public void providesAProcessEnvironment() {
-        assertThat(registry.get(ProcessEnvironment.class), notNullValue());
-    }
-
-    @Test
-    public void providesAFileSystem() {
-        assertThat(registry.get(FileSystem.class), notNullValue());
-    }
-
-    @Test
-    public void providesADocumentationRegistry() throws Exception {
-        assertThat(registry.get(DocumentationRegistry.class), instanceOf(DocumentationRegistry.class));
-    }
-}
diff --git a/subprojects/core/src/test/groovy/org/gradle/api/internal/project/GradleInternalServiceRegistryTest.groovy b/subprojects/core/src/test/groovy/org/gradle/api/internal/project/GradleInternalServiceRegistryTest.groovy
deleted file mode 100644
index 9902974..0000000
--- a/subprojects/core/src/test/groovy/org/gradle/api/internal/project/GradleInternalServiceRegistryTest.groovy
+++ /dev/null
@@ -1,102 +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.project;
-
-
-import org.gradle.StartParameter
-import org.gradle.api.internal.DocumentationRegistry
-import org.gradle.api.internal.GradleInternal
-import org.gradle.api.internal.plugins.DefaultPluginRegistry
-import org.gradle.api.internal.plugins.PluginRegistry
-import org.gradle.cache.CacheRepository
-import org.gradle.execution.BuildExecuter
-import org.gradle.execution.DefaultBuildExecuter
-import org.gradle.execution.TaskGraphExecuter
-import org.gradle.execution.taskgraph.DefaultTaskGraphExecuter
-import org.gradle.internal.service.ServiceRegistry
-import org.gradle.listener.ListenerManager
-import org.gradle.util.MultiParentClassLoader
-import spock.lang.Specification
-
-import static org.hamcrest.Matchers.sameInstance
-
-public class GradleInternalServiceRegistryTest extends Specification {
-    private GradleInternal gradle = Mock()
-    private ServiceRegistry parent = Mock()
-    private ListenerManager listenerManager = Mock()
-    private CacheRepository cacheRepository = Mock()
-    private GradleInternalServiceRegistry registry = new GradleInternalServiceRegistry(parent, gradle)
-    private StartParameter startParameter = new StartParameter()
-
-    public void setup() {
-        parent.get(StartParameter) >> Mock(StartParameter)
-        parent.get(ListenerManager) >> listenerManager
-        parent.get(CacheRepository) >> cacheRepository
-        parent.get(DocumentationRegistry) >> Mock(DocumentationRegistry)
-        gradle.getStartParameter() >> startParameter
-        gradle.getScriptClassLoader() >> new MultiParentClassLoader()
-    }
-
-    def "can create services for a project instance"() {
-        ProjectInternal project = Mock()
-
-        when:
-        ServiceRegistryFactory serviceRegistry = registry.createFor(project)
-
-        then:
-        serviceRegistry instanceof ProjectInternalServiceRegistry
-    }
-
-    def "provides a project registry"() {
-        when:
-        def projectRegistry = registry.get(IProjectRegistry)
-        def secondRegistry = registry.get(IProjectRegistry)
-
-        then:
-        projectRegistry instanceof DefaultProjectRegistry
-        projectRegistry sameInstance(secondRegistry)
-    }
-
-    def "provides a plugin registry"() {
-        when:
-        def pluginRegistry = registry.get(PluginRegistry)
-        def secondRegistry = registry.get(PluginRegistry)
-
-        then:
-        pluginRegistry instanceof DefaultPluginRegistry
-        secondRegistry sameInstance(pluginRegistry)
-    }
-
-    def "provides a build executer"() {
-        when:
-        def buildExecuter = registry.get(BuildExecuter)
-        def secondExecuter = registry.get(BuildExecuter)
-
-        then:
-        buildExecuter instanceof DefaultBuildExecuter
-        buildExecuter sameInstance(secondExecuter)
-    }
-
-    def "provides a task graph executer"() {
-        when:
-        def graphExecuter = registry.get(TaskGraphExecuter)
-        def secondExecuter = registry.get(TaskGraphExecuter)
-
-        then:
-        graphExecuter instanceof DefaultTaskGraphExecuter
-        graphExecuter sameInstance(secondExecuter)
-    }
-}
diff --git a/subprojects/core/src/test/groovy/org/gradle/api/internal/project/NewDefaultProjectTest.groovy b/subprojects/core/src/test/groovy/org/gradle/api/internal/project/NewDefaultProjectTest.groovy
index 7584cac..3c9a0bb 100644
--- a/subprojects/core/src/test/groovy/org/gradle/api/internal/project/NewDefaultProjectTest.groovy
+++ b/subprojects/core/src/test/groovy/org/gradle/api/internal/project/NewDefaultProjectTest.groovy
@@ -19,15 +19,12 @@ package org.gradle.api.internal.project
 import org.gradle.api.artifacts.dsl.ArtifactHandler
 import org.gradle.api.artifacts.dsl.DependencyHandler
 import org.gradle.api.internal.artifacts.configurations.ConfigurationContainerInternal
-import org.gradle.util.HelperUtil
+import org.gradle.util.TestUtil
 import spock.lang.Specification
 
-/**
- * @author Szczepan Faber
- */
 class NewDefaultProjectTest extends Specification {
 
-    def project = HelperUtil.createRootProject()
+    def project = TestUtil.createRootProject()
 
     void "delegates to artifacts handler"() {
         def handler = Mock(ArtifactHandler)
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 077e2b9..798f3a5 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
@@ -24,11 +24,12 @@ import org.gradle.api.internal.GradleInternal;
 import org.gradle.groovy.scripts.StringScriptSource;
 import org.gradle.groovy.scripts.UriScriptSource;
 import org.gradle.internal.Factory;
+import org.gradle.internal.service.scopes.ServiceRegistryFactory;
 import org.gradle.test.fixtures.file.TestNameTestDirectoryProvider;
-import org.gradle.testfixtures.internal.GlobalTestServices;
-import org.gradle.testfixtures.internal.TestTopLevelBuildServiceRegistry;
+import org.gradle.testfixtures.internal.TestBuildScopeServices;
+import org.gradle.testfixtures.internal.TestGlobalScopeServices;
 import org.gradle.util.JUnit4GroovyMockery;
-import org.gradle.util.MultiParentClassLoader;
+import org.gradle.internal.classloader.MultiParentClassLoader;
 import org.jmock.Expectations;
 import org.jmock.integration.junit4.JMock;
 import org.jmock.integration.junit4.JUnit4Mockery;
@@ -43,9 +44,6 @@ import java.io.IOException;
 import static org.hamcrest.Matchers.*;
 import static org.junit.Assert.*;
 
-/**
- * @author Hans Dockter
- */
 @RunWith(JMock.class)
 public class ProjectFactoryTest {
     private final JUnit4Mockery context = new JUnit4GroovyMockery();
@@ -57,7 +55,7 @@ public class ProjectFactoryTest {
     private Factory<RepositoryHandler> repositoryHandlerFactory = context.mock(Factory.class);
     private RepositoryHandler repositoryHandler = context.mock(RepositoryHandler.class);
     private StartParameter startParameterStub = new StartParameter();
-    private ServiceRegistryFactory serviceRegistryFactory = new TestTopLevelBuildServiceRegistry(new GlobalTestServices(), startParameterStub, rootDir);
+    private ServiceRegistryFactory serviceRegistryFactory = new TestBuildScopeServices(new TestGlobalScopeServices(), startParameterStub, rootDir);
     private org.gradle.internal.reflect.Instantiator instantiatorMock = serviceRegistryFactory.get(org.gradle.internal.reflect.Instantiator.class);
     private GradleInternal gradle = context.mock(GradleInternal.class);
 
@@ -77,9 +75,7 @@ public class ProjectFactoryTest {
             allowing(gradle).getStartParameter();
             will(returnValue(startParameterStub));
             allowing(gradle).getProjectRegistry();
-            will(returnValue(gradleServices.get(IProjectRegistry.class)));
-            allowing(gradle).getScriptClassLoader();
-            will(returnValue(buildScriptClassLoader));
+            will(returnValue(gradleServices.get(ProjectRegistry.class)));
             allowing(gradle).getGradleUserHomeDir();
             will(returnValue(new File("gradleUserHomeDir")));
             ignoring(gradle).getProjectEvaluationBroadcaster();
diff --git a/subprojects/core/src/test/groovy/org/gradle/api/internal/project/ProjectInternalServiceRegistryTest.java b/subprojects/core/src/test/groovy/org/gradle/api/internal/project/ProjectInternalServiceRegistryTest.java
deleted file mode 100644
index 2894370..0000000
--- a/subprojects/core/src/test/groovy/org/gradle/api/internal/project/ProjectInternalServiceRegistryTest.java
+++ /dev/null
@@ -1,276 +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;
-
-import org.gradle.api.AntBuilder;
-import org.gradle.api.artifacts.dsl.ArtifactHandler;
-import org.gradle.api.artifacts.dsl.DependencyHandler;
-import org.gradle.api.artifacts.dsl.RepositoryHandler;
-import org.gradle.api.initialization.dsl.ScriptHandler;
-import org.gradle.api.internal.*;
-import org.gradle.api.internal.artifacts.ArtifactPublicationServices;
-import org.gradle.api.internal.artifacts.DependencyManagementServices;
-import org.gradle.api.internal.artifacts.DependencyResolutionServices;
-import org.gradle.api.internal.artifacts.configurations.ConfigurationContainerInternal;
-import org.gradle.api.internal.artifacts.configurations.DependencyMetaDataProvider;
-import org.gradle.api.internal.artifacts.dsl.dependencies.DependencyFactory;
-import org.gradle.api.internal.artifacts.dsl.dependencies.ProjectFinder;
-import org.gradle.api.internal.file.*;
-import org.gradle.api.internal.initialization.DefaultScriptHandler;
-import org.gradle.api.internal.initialization.ScriptClassLoaderProvider;
-import org.gradle.api.internal.plugins.DefaultProjectsPluginContainer;
-import org.gradle.api.internal.plugins.PluginRegistry;
-import org.gradle.api.internal.project.taskfactory.ITaskFactory;
-import org.gradle.api.internal.tasks.DefaultTaskContainerFactory;
-import org.gradle.api.internal.tasks.TaskContainerInternal;
-import org.gradle.api.logging.LoggingManager;
-import org.gradle.api.plugins.PluginContainer;
-import org.gradle.initialization.ProjectAccessListener;
-import org.gradle.internal.Factory;
-import org.gradle.internal.nativeplatform.filesystem.FileSystem;
-import org.gradle.internal.reflect.DirectInstantiator;
-import org.gradle.internal.reflect.Instantiator;
-import org.gradle.internal.service.ServiceRegistry;
-import org.gradle.logging.LoggingManagerInternal;
-import org.gradle.util.JUnit4GroovyMockery;
-import org.hamcrest.Matcher;
-import org.hamcrest.Matchers;
-import org.jmock.Expectations;
-import org.jmock.integration.junit4.JMock;
-import org.jmock.integration.junit4.JUnit4Mockery;
-import org.junit.Before;
-import org.junit.Test;
-import org.junit.runner.RunWith;
-
-import java.io.File;
-
-import static org.hamcrest.Matchers.*;
-import static org.junit.Assert.assertThat;
-
- at RunWith(JMock.class)
-public class ProjectInternalServiceRegistryTest {
-    private final JUnit4Mockery context = new JUnit4GroovyMockery();
-    private final ProjectInternal project = context.mock(ProjectInternal.class);
-    private final ConfigurationContainerInternal configurationContainer = context.mock(ConfigurationContainerInternal.class);
-    private final GradleInternal gradle = context.mock(GradleInternal.class);
-    private final DependencyManagementServices dependencyManagementServices = context.mock(DependencyManagementServices.class);
-    private final ITaskFactory taskFactory = context.mock(ITaskFactory.class);
-    private final DependencyFactory dependencyFactory = context.mock(DependencyFactory.class);
-    private final ServiceRegistry parent = context.mock(ServiceRegistry.class);
-    private final ProjectInternalServiceRegistry registry = new ProjectInternalServiceRegistry(parent, project);
-    private final PluginRegistry pluginRegistry = context.mock(PluginRegistry.class);
-    private final DependencyResolutionServices dependencyResolutionServices = context.mock(DependencyResolutionServices.class);
-    private final RepositoryHandler repositoryHandler = context.mock(RepositoryHandler.class);
-    private final ArtifactPublicationServices publicationServices = context.mock(ArtifactPublicationServices.class);
-    private final DependencyHandler dependencyHandler = context.mock(DependencyHandler.class);
-    private final ArtifactHandler artifactHandler = context.mock(ArtifactHandler.class);
-    private final DirectInstantiator instantiator = new DirectInstantiator();
-
-    @Before
-    public void setUp() {
-        context.checking(new Expectations() {{
-            allowing(project).getGradle();
-            will(returnValue(gradle));
-            allowing(project).getProjectDir();
-            will(returnValue(new File("project-dir").getAbsoluteFile()));
-            allowing(project).getBuildScriptSource();
-            allowing(parent).get(ITaskFactory.class);
-            will(returnValue(taskFactory));
-            allowing(parent).get(DependencyFactory.class);
-            will(returnValue(dependencyFactory));
-            allowing(parent).get(PluginRegistry.class);
-            will(returnValue(pluginRegistry));
-            allowing(parent).get(DependencyManagementServices.class);
-            will(returnValue(dependencyManagementServices));
-            allowing(parent).get(org.gradle.internal.reflect.Instantiator.class);
-            will(returnValue(instantiator));
-            allowing(parent).get(FileSystem.class);
-            will(returnValue(context.mock(FileSystem.class)));
-            allowing(parent).get(ClassGenerator.class);
-            will(returnValue(context.mock(ClassGenerator.class)));
-            allowing(parent).get(ProjectAccessListener.class);
-            will(returnValue(context.mock(ProjectAccessListener.class)));
-        }});
-    }
-
-    @Test
-    public void createsARegistryForATask() {
-        ServiceRegistryFactory taskRegistry = registry.createFor(context.mock(TaskInternal.class));
-        assertThat(taskRegistry, instanceOf(TaskInternalServiceRegistry.class));
-    }
-
-    @Test
-    public void providesATaskContainerFactory() {
-        final ITaskFactory childFactory = context.mock(ITaskFactory.class);
-
-        context.checking(new Expectations() {{
-            Matcher matcher = instanceOf(ClassGeneratorBackedInstantiator.class);
-            one(taskFactory).createChild(with(sameInstance(project)), with((Matcher<Instantiator>)matcher));
-            will(returnValue(childFactory));
-        }});
-
-        assertThat(registry.getFactory(TaskContainerInternal.class), instanceOf(DefaultTaskContainerFactory.class));
-    }
-
-    @Test
-    public void providesAPluginContainer() {
-        expectScriptClassLoaderProviderCreated();
-        context.checking(new Expectations() {{
-            Matcher matcher = Matchers.instanceOf(DependencyInjectingInstantiator.class);
-            one(pluginRegistry).createChild(with(notNullValue(ClassLoader.class)), with((Matcher<Instantiator>)matcher));
-        }});
-
-        assertThat(registry.get(PluginContainer.class), instanceOf(DefaultProjectsPluginContainer.class));
-        assertThat(registry.get(PluginContainer.class), sameInstance(registry.get(PluginContainer.class)));
-    }
-
-    @Test
-    public void providesAnArtifactPublicationServicesFactory() {
-        expectDependencyResolutionServicesCreated();
-
-        assertThat(registry.get(ArtifactPublicationServices.class), sameInstance(publicationServices));
-    }
-
-    @Test
-    public void providesARepositoryHandler() {
-        expectDependencyResolutionServicesCreated();
-
-        assertThat(registry.get(RepositoryHandler.class), sameInstance(repositoryHandler));
-        assertThat(registry.get(RepositoryHandler.class), sameInstance(registry.get(RepositoryHandler.class)));
-    }
-
-    @Test
-    public void providesAConfigurationContainer() {
-        expectDependencyResolutionServicesCreated();
-
-        assertThat(registry.get(ConfigurationContainerInternal.class), sameInstance(configurationContainer));
-        assertThat(registry.get(ConfigurationContainerInternal.class), sameInstance(registry.get(ConfigurationContainerInternal.class)));
-    }
-
-    @Test
-    public void providesAnArtifactHandler() {
-        expectDependencyResolutionServicesCreated();
-
-        assertThat(registry.get(ArtifactHandler.class), sameInstance(artifactHandler));
-        assertThat(registry.get(ArtifactHandler.class), sameInstance(registry.get(ArtifactHandler.class)));
-    }
-
-    @Test
-    public void providesADependencyHandler() {
-        expectDependencyResolutionServicesCreated();
-
-        assertThat(registry.get(DependencyHandler.class), sameInstance(dependencyHandler));
-        assertThat(registry.get(DependencyHandler.class), sameInstance(registry.get(DependencyHandler.class)));
-    }
-
-    @Test
-    public void providesAnAntBuilderFactory() {
-        assertThat(registry.getFactory(AntBuilder.class), instanceOf(DefaultAntBuilderFactory.class));
-        assertThat(registry.getFactory(AntBuilder.class), sameInstance((Object) registry.getFactory(AntBuilder.class)));
-    }
-
-    @Test
-    public void providesAScriptHandlerAndScriptClassLoaderProvider() {
-        expectScriptClassLoaderProviderCreated();
-
-        assertThat(registry.get(ScriptHandler.class), instanceOf(DefaultScriptHandler.class));
-        assertThat(registry.get(ScriptHandler.class), sameInstance(registry.get(ScriptHandler.class)));
-        assertThat(registry.get(ScriptClassLoaderProvider.class), sameInstance((Object) registry.get(
-                ScriptHandler.class)));
-    }
-
-    @Test
-    public void providesAFileResolver() {
-        assertThat(registry.get(FileResolver.class), instanceOf(BaseDirFileResolver.class));
-        assertThat(registry.get(FileResolver.class), sameInstance(registry.get(FileResolver.class)));
-    }
-
-    @Test
-    public void providesAFileOperationsInstance() {
-        context.checking(new Expectations(){{
-            one(project).getTasks();
-        }});
-
-        assertThat(registry.get(FileOperations.class), instanceOf(DefaultFileOperations.class));
-        assertThat(registry.get(FileOperations.class), sameInstance(registry.get(FileOperations.class)));
-    }
-    
-    @Test
-    public void providesATemporaryFileProvider() {
-        assertThat(registry.get(TemporaryFileProvider.class), instanceOf(DefaultTemporaryFileProvider.class));
-        assertThat(registry.get(TemporaryFileProvider.class), sameInstance(registry.get(TemporaryFileProvider.class)));
-    }
-    
-    @Test
-    public void providesALoggingManager() {
-        final Factory<LoggingManagerInternal> loggingManagerFactory = context.mock(Factory.class);
-        final LoggingManager loggingManager = context.mock(LoggingManagerInternal.class);
-
-        context.checking(new Expectations(){{
-            allowing(parent).getFactory(LoggingManagerInternal.class);
-            will(returnValue(loggingManagerFactory));
-            one(loggingManagerFactory).create();
-            will(returnValue(loggingManager));
-        }});
-
-        assertThat(registry.get(LoggingManager.class), sameInstance(loggingManager));
-        assertThat(registry.get(LoggingManager.class), sameInstance(registry.get(LoggingManager.class)));
-    }
-
-    private void expectScriptClassLoaderProviderCreated() {
-        context.checking(new Expectations() {{
-            one(dependencyManagementServices).create(with(notNullValue(FileResolver.class)),
-                    with(notNullValue(DependencyMetaDataProvider.class)),
-                    with(notNullValue(ProjectFinder.class)),
-                    with(notNullValue(DomainObjectContext.class)));
-            will(returnValue(dependencyResolutionServices));
-
-            ignoring(dependencyResolutionServices);
-
-            allowing(project).getParent();
-            will(returnValue(null));
-
-            allowing(gradle).getScriptClassLoader();
-            will(returnValue(null));
-        }});
-    }
-
-    private void expectDependencyResolutionServicesCreated() {
-        context.checking(new Expectations(){{
-            one(dependencyManagementServices).create(with(notNullValue(FileResolver.class)),
-                    with(notNullValue(DependencyMetaDataProvider.class)),
-                    with(notNullValue(ProjectFinder.class)),
-                    with(notNullValue(DomainObjectContext.class)));
-            will(returnValue(dependencyResolutionServices));
-
-            allowing(dependencyResolutionServices).getResolveRepositoryHandler();
-            will(returnValue(repositoryHandler));
-
-            allowing(dependencyResolutionServices).createArtifactPublicationServices();
-            will(returnValue(publicationServices));
-
-            allowing(dependencyResolutionServices).getConfigurationContainer();
-            will(returnValue(configurationContainer));
-
-            allowing(dependencyResolutionServices).getDependencyHandler();
-            will(returnValue(dependencyHandler));
-
-            allowing(dependencyResolutionServices).getArtifactHandler();
-            will(returnValue(artifactHandler));
-        }});
-    }
-}
diff --git a/subprojects/core/src/test/groovy/org/gradle/api/internal/project/TaskExecutionServicesTest.groovy b/subprojects/core/src/test/groovy/org/gradle/api/internal/project/TaskExecutionServicesTest.groovy
deleted file mode 100644
index a73b72b..0000000
--- a/subprojects/core/src/test/groovy/org/gradle/api/internal/project/TaskExecutionServicesTest.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.api.internal.project
-
-import spock.lang.Specification
-import org.gradle.api.internal.tasks.TaskExecuter
-import org.gradle.api.internal.tasks.execution.ExecuteAtMostOnceTaskExecuter
-import org.gradle.listener.ListenerManager
-import org.gradle.cache.CacheRepository
-import org.gradle.StartParameter
-import org.gradle.api.invocation.Gradle
-import org.gradle.cache.DirectoryCacheBuilder
-import org.gradle.cache.PersistentCache
-import org.gradle.internal.service.ServiceRegistry
-
-class TaskExecutionServicesTest extends Specification {
-    final ServiceRegistry parent = Mock()
-    final Gradle gradle = Mock()
-    final TaskExecutionServices services = new TaskExecutionServices(parent, gradle)
-
-    def "makes a TaskExecutor available"() {
-        given:
-        ListenerManager listenerManager = Mock()
-        StartParameter startParameter = Mock()
-        CacheRepository cacheRepository = Mock()
-        DirectoryCacheBuilder cacheBuilder = Mock()
-        PersistentCache cache = Mock()
-        _ * parent.get(ListenerManager) >> listenerManager
-        _ * parent.get(StartParameter) >> startParameter
-        _ * parent.get(CacheRepository) >> cacheRepository
-        _ * cacheRepository.cache(!null) >> cacheBuilder
-        _ * cacheBuilder.forObject(gradle) >> cacheBuilder
-        _ * cacheBuilder.withDisplayName(!null) >> cacheBuilder
-        _ * cacheBuilder.withLockMode(!null) >> cacheBuilder
-        _ * cacheBuilder.open() >> cache
-
-        expect:
-        services.get(TaskExecuter) instanceof ExecuteAtMostOnceTaskExecuter
-        services.get(TaskExecuter).is(services.get(TaskExecuter))
-    }
-}
diff --git a/subprojects/core/src/test/groovy/org/gradle/api/internal/project/TaskInternalServiceRegistryTest.java b/subprojects/core/src/test/groovy/org/gradle/api/internal/project/TaskInternalServiceRegistryTest.java
deleted file mode 100644
index 1844816..0000000
--- a/subprojects/core/src/test/groovy/org/gradle/api/internal/project/TaskInternalServiceRegistryTest.java
+++ /dev/null
@@ -1,90 +0,0 @@
-/*
- * Copyright 2010 the original author or authors.
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- *      http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package org.gradle.api.internal.project;
-
-import org.gradle.api.internal.TaskInternal;
-import org.gradle.api.internal.TaskOutputsInternal;
-import org.gradle.api.internal.file.FileResolver;
-import org.gradle.api.internal.tasks.DefaultTaskInputs;
-import org.gradle.api.internal.tasks.DefaultTaskOutputs;
-import org.gradle.api.internal.tasks.TaskStatusNagger;
-import org.gradle.api.logging.LoggingManager;
-import org.gradle.api.tasks.TaskInputs;
-import org.gradle.internal.Factory;
-import org.gradle.internal.service.ServiceRegistry;
-import org.gradle.logging.LoggingManagerInternal;
-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 junit.framework.Assert.assertSame;
-import static org.hamcrest.Matchers.instanceOf;
-import static org.hamcrest.Matchers.sameInstance;
-import static org.junit.Assert.assertThat;
-
- at RunWith(JMock.class)
-public class TaskInternalServiceRegistryTest {
-    private final JUnit4Mockery context = new JUnit4Mockery();
-    private final ServiceRegistry parent = context.mock(ServiceRegistry.class);
-    private final ProjectInternal project = context.mock(ProjectInternal.class);
-    private final TaskInternal task = context.mock(TaskInternal.class);
-    private final TaskInternalServiceRegistry registry = new TaskInternalServiceRegistry(parent, project, task);
-
-    @Before
-    public void setUp() {
-        context.checking(new Expectations() {{
-            allowing(project).getFileResolver();
-            will(returnValue(context.mock(FileResolver.class)));
-        }});
-    }
-
-    @Test
-    public void createsATaskInputsInstance() {
-        TaskInputs inputs = registry.get(TaskInputs.class);
-        assertThat(inputs, instanceOf(DefaultTaskInputs.class));
-    }
-
-    @Test
-    public void createsATaskOutputsInternalInstance() {
-        TaskOutputsInternal outputs = registry.get(TaskOutputsInternal.class);
-        assertThat(outputs, instanceOf(DefaultTaskOutputs.class));
-    }
-
-    @Test
-    public void createsATaskStatusNaggerInstance() {
-        TaskStatusNagger nagger = registry.get(TaskStatusNagger.class);
-        assertSame(nagger, registry.get(TaskStatusNagger.class));
-    }
-
-    @Test
-    public void createsALoggingManagerAndStdOutputCapture() {
-        final Factory<LoggingManagerInternal> loggingManagerFactory = context.mock(Factory.class);
-        final LoggingManager loggingManager = context.mock(LoggingManagerInternal.class);
-
-        context.checking(new Expectations() {{
-            allowing(parent).getFactory(LoggingManagerInternal.class);
-            will(returnValue(loggingManagerFactory));
-            one(loggingManagerFactory).create();
-            will(returnValue(loggingManager));
-        }});
-
-        assertThat(registry.get(LoggingManager.class), sameInstance(loggingManager));
-    }
-}
diff --git a/subprojects/core/src/test/groovy/org/gradle/api/internal/project/TestPlugin1.groovy b/subprojects/core/src/test/groovy/org/gradle/api/internal/project/TestPlugin1.groovy
index 3dc70b5..6dc5e3d 100644
--- a/subprojects/core/src/test/groovy/org/gradle/api/internal/project/TestPlugin1.groovy
+++ b/subprojects/core/src/test/groovy/org/gradle/api/internal/project/TestPlugin1.groovy
@@ -19,9 +19,6 @@ package org.gradle.api.internal.project
 import org.gradle.api.Plugin
 import org.gradle.api.Project
 
-/**
-* @author Hans Dockter
-*/
 class TestPlugin1 implements Plugin<Project> {
 
     int applyCounter = 0
diff --git a/subprojects/core/src/test/groovy/org/gradle/api/internal/project/TestPlugin2.groovy b/subprojects/core/src/test/groovy/org/gradle/api/internal/project/TestPlugin2.groovy
index b68f716..87bde21 100644
--- a/subprojects/core/src/test/groovy/org/gradle/api/internal/project/TestPlugin2.groovy
+++ b/subprojects/core/src/test/groovy/org/gradle/api/internal/project/TestPlugin2.groovy
@@ -19,9 +19,6 @@ package org.gradle.api.internal.project
 import org.gradle.api.Plugin
 import org.gradle.api.Project
 
-/**
-* @author Hans Dockter
-*/
 class TestPlugin2 implements Plugin<Project> {
     void apply(Project project) {
     }
diff --git a/subprojects/core/src/test/groovy/org/gradle/api/internal/project/TopLevelBuildServiceRegistryTest.groovy b/subprojects/core/src/test/groovy/org/gradle/api/internal/project/TopLevelBuildServiceRegistryTest.groovy
deleted file mode 100644
index 6cc5a13..0000000
--- a/subprojects/core/src/test/groovy/org/gradle/api/internal/project/TopLevelBuildServiceRegistryTest.groovy
+++ /dev/null
@@ -1,264 +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
-
-import org.gradle.StartParameter
-import org.gradle.api.internal.*
-import org.gradle.api.internal.classpath.DefaultModuleRegistry
-import org.gradle.api.internal.classpath.ModuleRegistry
-import org.gradle.api.internal.classpath.PluginModuleRegistry
-import org.gradle.cache.CacheRepository
-import org.gradle.cache.internal.CacheFactory
-import org.gradle.cache.internal.DefaultCacheRepository
-import org.gradle.configuration.BuildConfigurer
-import org.gradle.configuration.DefaultBuildConfigurer
-import org.gradle.configuration.DefaultScriptPluginFactory
-import org.gradle.configuration.ScriptPluginFactory
-import org.gradle.groovy.scripts.DefaultScriptCompilerFactory
-import org.gradle.groovy.scripts.ScriptCompilerFactory
-import org.gradle.initialization.*
-import org.gradle.internal.Factory
-import org.gradle.internal.concurrent.DefaultExecutorFactory
-import org.gradle.internal.concurrent.ExecutorFactory
-import org.gradle.internal.reflect.Instantiator
-import org.gradle.internal.service.ServiceRegistry
-import org.gradle.listener.DefaultListenerManager
-import org.gradle.listener.ListenerManager
-import org.gradle.logging.LoggingManagerInternal
-import org.gradle.messaging.remote.MessagingServer
-import org.gradle.process.internal.DefaultWorkerProcessFactory
-import org.gradle.process.internal.WorkerProcessBuilder
-import org.gradle.profile.ProfileEventAdapter
-import org.gradle.test.fixtures.file.TestNameTestDirectoryProvider
-import org.gradle.util.ClassLoaderFactory
-import org.gradle.util.MultiParentClassLoader
-import org.junit.Rule
-import spock.lang.Specification
-import spock.lang.Timeout
-
-import static org.hamcrest.Matchers.instanceOf
-import static org.hamcrest.Matchers.sameInstance
-import static org.junit.Assert.assertThat
-
-public class TopLevelBuildServiceRegistryTest extends Specification {
-    @Rule
-    TestNameTestDirectoryProvider tmpDir = new TestNameTestDirectoryProvider()
-    StartParameter startParameter = new StartParameter()
-    ServiceRegistry parent = Mock()
-    Factory<CacheFactory> cacheFactoryFactory = Mock()
-    ClosableCacheFactory cacheFactory = Mock()
-    ClassLoaderRegistry classLoaderRegistry = Mock()
-
-    TopLevelBuildServiceRegistry registry = new TopLevelBuildServiceRegistry(parent, startParameter)
-
-    def setup() {
-        startParameter.gradleUserHomeDir = tmpDir.testDirectory
-        parent.getFactory(CacheFactory) >> cacheFactoryFactory
-        cacheFactoryFactory.create() >> cacheFactory
-        parent.get(ClassLoaderRegistry) >> classLoaderRegistry
-        parent.getFactory(LoggingManagerInternal) >> Mock(Factory)
-        parent.get(ModuleRegistry) >> new DefaultModuleRegistry()
-        parent.get(PluginModuleRegistry) >> Mock(PluginModuleRegistry)
-        parent.get(Instantiator) >> ThreadGlobalInstantiator.getOrCreate()
-    }
-
-    def delegatesToParentForUnknownService() {
-        setup:
-        parent.get(String) >> "value"
-
-        expect:
-        registry.get(String) == "value"
-    }
-
-    def throwsExceptionForUnknownDomainObject() {
-        when:
-        registry.createFor("string")
-        then:
-        def e = thrown(IllegalArgumentException)
-        e.message == "Cannot create services for unknown domain object of type String."
-    }
-
-    def canCreateServicesForAGradleInstance() {
-        setup:
-        GradleInternal gradle = Mock()
-        ServiceRegistryFactory registry = this.registry.createFor(gradle)
-        expect:
-        registry instanceof GradleInternalServiceRegistry
-    }
-
-    def providesAListenerManager() {
-        setup:
-        ListenerManager listenerManager = expectListenerManagerCreated()
-        expect:
-        assertThat(registry.get(ListenerManager), sameInstance(listenerManager))
-    }
-
-    @Timeout(5)
-    def providesAScriptCompilerFactory() {
-        setup:
-        expectListenerManagerCreated()
-
-        expect:
-        registry.get(ScriptCompilerFactory) instanceof DefaultScriptCompilerFactory
-        registry.get(ScriptCompilerFactory) == registry.get(ScriptCompilerFactory)
-    }
-
-    def providesACacheRepositoryAndCleansUpOnClose() {
-        setup:
-        1 * cacheFactory.close()
-
-        expect:
-        registry.get(CacheRepository) instanceof DefaultCacheRepository
-        registry.get(CacheRepository) == registry.get(CacheRepository)
-        registry.close()
-    }
-
-    def providesAnInitScriptHandler() {
-        setup:
-        allowGetCoreImplClassLoader()
-        expectScriptClassLoaderCreated()
-        expectListenerManagerCreated()
-        allowGetGradleDistributionLocator()
-
-        expect:
-        registry.get(InitScriptHandler) instanceof InitScriptHandler
-        registry.get(InitScriptHandler) == registry.get(InitScriptHandler)
-    }
-
-    def providesAScriptObjectConfigurerFactory() {
-        setup:
-        allowGetCoreImplClassLoader()
-        expectListenerManagerCreated()
-        expectScriptClassLoaderCreated()
-        expect:
-        assertThat(registry.get(ScriptPluginFactory), instanceOf(DefaultScriptPluginFactory))
-        assertThat(registry.get(ScriptPluginFactory), sameInstance(registry.get(ScriptPluginFactory)))
-    }
-
-    def providesASettingsProcessor() {
-        setup:
-        allowGetCoreImplClassLoader()
-        expectListenerManagerCreated()
-        expectScriptClassLoaderCreated()
-        expect:
-        assertThat(registry.get(SettingsProcessor), instanceOf(PropertiesLoadingSettingsProcessor))
-        assertThat(registry.get(SettingsProcessor), sameInstance(registry.get(SettingsProcessor)))
-    }
-
-    def providesAnExceptionAnalyser() {
-        setup:
-        expectListenerManagerCreated()
-        expect:
-        assertThat(registry.get(ExceptionAnalyser), instanceOf(MultipleBuildFailuresExceptionAnalyser))
-        assertThat(registry.get(ExceptionAnalyser).delegate, instanceOf(DefaultExceptionAnalyser))
-        assertThat(registry.get(ExceptionAnalyser), sameInstance(registry.get(ExceptionAnalyser)))
-    }
-
-    def providesAWorkerProcessFactory() {
-        setup:
-        expectParentServiceLocated(MessagingServer)
-        allowGetCoreImplClassLoader()
-
-        expect:
-        assertThat(registry.getFactory(WorkerProcessBuilder), instanceOf(DefaultWorkerProcessFactory))
-    }
-
-    def providesAnIsolatedAntBuilder() {
-        setup:
-        expectParentServiceLocated(ClassLoaderFactory)
-        allowGetCoreImplClassLoader()
-        expect:
-
-        assertThat(registry.get(IsolatedAntBuilder), instanceOf(DefaultIsolatedAntBuilder))
-        assertThat(registry.get(IsolatedAntBuilder), sameInstance(registry.get(IsolatedAntBuilder)))
-    }
-
-    def providesAProjectFactory() {
-        setup:
-        expectParentServiceLocated(Instantiator)
-        expectParentServiceLocated(ClassGenerator)
-        expect:
-        assertThat(registry.get(IProjectFactory), instanceOf(ProjectFactory))
-        assertThat(registry.get(IProjectFactory), sameInstance(registry.get(IProjectFactory)))
-    }
-
-    def providesAnExecutorFactory() {
-        expect:
-        assertThat(registry.get(ExecutorFactory), instanceOf(DefaultExecutorFactory))
-        assertThat(registry.get(ExecutorFactory), sameInstance(registry.get(ExecutorFactory)))
-    }
-
-    def providesABuildConfigurer() {
-        expect:
-        assertThat(registry.get(BuildConfigurer), instanceOf(DefaultBuildConfigurer))
-        assertThat(registry.get(BuildConfigurer), sameInstance(registry.get(BuildConfigurer)))
-    }
-
-    def providesAPropertiesLoader() {
-        expect:
-        assertThat(registry.get(IGradlePropertiesLoader), instanceOf(DefaultGradlePropertiesLoader))
-        assertThat(registry.get(IGradlePropertiesLoader), sameInstance(registry.get(IGradlePropertiesLoader)))
-    }
-
-    def providesABuildLoader() {
-        setup:
-        expectParentServiceLocated(Instantiator)
-        expect:
-        assertThat(registry.get(BuildLoader), instanceOf(ProjectPropertySettingBuildLoader))
-        assertThat(registry.get(BuildLoader), sameInstance(registry.get(BuildLoader)))
-    }
-
-    def providesAProfileEventAdapter() {
-        setup:
-        expectParentServiceLocated(BuildRequestMetaData)
-        expectListenerManagerCreated()
-
-        expect:
-        assertThat(registry.get(ProfileEventAdapter), instanceOf(ProfileEventAdapter))
-        assertThat(registry.get(ProfileEventAdapter), sameInstance(registry.get(ProfileEventAdapter)))
-    }
-
-    private <T> T expectParentServiceLocated(Class<T> type) {
-        T t = Mock(type)
-        parent.get(type) >> t
-        t
-    }
-
-    private ListenerManager expectListenerManagerCreated() {
-        final ListenerManager listenerManager = new DefaultListenerManager()
-        final ListenerManager listenerManagerParent = Mock()
-        parent.get(ListenerManager) >> listenerManagerParent
-        1 * listenerManagerParent.createChild() >> listenerManager
-        listenerManager
-    }
-
-    private void allowGetCoreImplClassLoader() {
-        classLoaderRegistry.getCoreImplClassLoader() >> new ClassLoader() {}
-    }
-
-    private void expectScriptClassLoaderCreated() {
-        1 * classLoaderRegistry.createScriptClassLoader() >> new MultiParentClassLoader()
-    }
-
-    private void allowGetGradleDistributionLocator() {
-        parent.get(GradleDistributionLocator) >> Mock(GradleDistributionLocator)
-    }
-
-    public interface ClosableCacheFactory extends CacheFactory {
-        void close()
-    }
-}
\ No newline at end of file
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 032e97d..d8b9442 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
@@ -1,5 +1,5 @@
 /*
- * Copyright 2010 the original author or authors.
+ * Copyright 2013 the original author or authors.
  *
  * Licensed under the Apache License, Version 2.0 (the "License");
  * you may not use this file except in compliance with the License.
@@ -16,6 +16,7 @@
 
 package org.gradle.api.internal.project.taskfactory;
 
+import org.gradle.api.Action;
 import org.gradle.api.DefaultTask;
 import org.gradle.api.GradleException;
 import org.gradle.api.Task;
@@ -24,11 +25,11 @@ import org.gradle.api.internal.AbstractTask;
 import org.gradle.api.internal.TaskInternal;
 import org.gradle.api.internal.project.DefaultProject;
 import org.gradle.api.tasks.*;
+import org.gradle.api.tasks.incremental.IncrementalTaskInputs;
 import org.gradle.test.fixtures.file.TestFile;
 import org.gradle.test.fixtures.file.TestNameTestDirectoryProvider;
 import org.gradle.util.GFileUtils;
-import org.gradle.util.HelperUtil;
-import org.gradle.util.ReflectionUtil;
+import org.gradle.util.TestUtil;
 import org.jmock.Expectations;
 import org.jmock.integration.junit4.JMock;
 import org.jmock.integration.junit4.JUnit4Mockery;
@@ -36,11 +37,13 @@ import org.jmock.lib.legacy.ClassImposteriser;
 import org.junit.Rule;
 import org.junit.Test;
 import org.junit.runner.RunWith;
+import spock.lang.Issue;
 
 import java.io.File;
 import java.util.*;
 import java.util.concurrent.Callable;
 
+import static org.gradle.internal.reflect.JavaReflectionUtil.readField;
 import static org.gradle.util.Matchers.isEmpty;
 import static org.gradle.util.WrapUtil.toList;
 import static org.gradle.util.WrapUtil.toSet;
@@ -77,7 +80,7 @@ public class AnnotationProcessingTaskFactoryTest {
     }
 
     private <T extends Task> T expectTaskCreated(final Class<T> type, final Object... params) {
-        DefaultProject project = HelperUtil.createRootProject();
+        DefaultProject project = TestUtil.createRootProject();
         T task = AbstractTask.injectIntoNewInstance(project, "task", new Callable<T>() {
             public T call() throws Exception {
                 if (params.length > 0) {
@@ -139,11 +142,29 @@ public class AnnotationProcessingTaskFactoryTest {
     }
 
     @Test
+    public void createsContextualActionFoIncrementalTaskAction() {
+        final Action<IncrementalTaskInputs> action = context.mock(Action.class);
+        TaskWithIncrementalAction task = expectTaskCreated(TaskWithIncrementalAction.class, action);
+
+        context.checking(new Expectations() {{
+            one(action).execute(with(notNullValue(IncrementalTaskInputs.class)));
+        }});
+
+        task.execute();
+    }
+
+    @Test
+    public void failsWhenMultipleActionsAreIncremental() {
+        assertTaskCreationFails(TaskWithMultipleIncrementalActions.class,
+                "Cannot have multiple @TaskAction methods accepting an IncrementalTaskInputs parameter.");
+    }
+
+    @Test
     public void cachesClassMetaInfo() {
         TaskWithInputFile task = expectTaskCreated(TaskWithInputFile.class, existingFile);
         TaskWithInputFile task2 = expectTaskCreated(TaskWithInputFile.class, missingFile);
 
-        assertThat(ReflectionUtil.getProperty(task.getActions().get(0), "action"), sameInstance(ReflectionUtil.getProperty(task2.getActions().get(0), "action")));
+        assertThat(readField(task.getActions().get(0), Action.class, "action"), sameInstance(readField(task2.getActions().get(0), Action.class, "action")));
     }
     
     @Test
@@ -154,8 +175,14 @@ public class AnnotationProcessingTaskFactoryTest {
 
     @Test
     public void failsWhenMethodWithParametersHasTaskActionAnnotation() {
-        assertTaskCreationFails(TaskWithParamMethod.class,
-                "Cannot use @TaskAction annotation on method TaskWithParamMethod.doStuff() as this method takes parameters.");
+        assertTaskCreationFails(TaskWithMultiParamAction.class,
+                "Cannot use @TaskAction annotation on method TaskWithMultiParamAction.doStuff() as this method takes multiple parameters.");
+    }
+
+    @Test
+    public void failsWhenMethodWithInvalidParameterHasTaskActionAnnotation() {
+        assertTaskCreationFails(TaskWithSingleParamAction.class,
+                "Cannot use @TaskAction annotation on method TaskWithSingleParamAction.doStuff() because int is not a valid parameter to an action method.");
     }
 
     private void assertTaskCreationFails(Class<? extends Task> type, String message) {
@@ -565,6 +592,13 @@ public class AnnotationProcessingTaskFactoryTest {
     }
 
     @Test
+    @Issue("http://issues.gradle.org/browse/GRADLE-2815")
+    public void registersSpecifiedBooleanInputValue() {
+        TaskWithBooleanInput task = expectTaskCreated(TaskWithBooleanInput.class, true);
+        assertThat(task.getInputs().getProperties().get("inputValue"), equalTo((Object) true));
+    }
+
+    @Test
     public void validationActionSucceedsWhenPropertyMarkedWithOptionalAnnotationNotSpecified() {
         TaskWithOptionalInputFile task = expectTaskCreated(TaskWithOptionalInputFile.class);
         task.execute();
@@ -732,9 +766,39 @@ public class AnnotationProcessingTaskFactoryTest {
         }
     }
 
-    public static class TaskWithParamMethod extends DefaultTask {
+    public static class TaskWithIncrementalAction extends DefaultTask {
+        private final Action<IncrementalTaskInputs> action;
+
+        public TaskWithIncrementalAction(Action<IncrementalTaskInputs> action) {
+            this.action = action;
+        }
+
+        @TaskAction
+        public void doStuff(IncrementalTaskInputs changes) {
+            action.execute(changes);
+        }
+    }
+
+    public static class TaskWithMultipleIncrementalActions extends DefaultTask {
+
+        @TaskAction
+        public void doStuff(IncrementalTaskInputs changes) {
+        }
+
+        @TaskAction
+        public void doStuff2(IncrementalTaskInputs changes) {
+        }
+    }
+
+    public static class TaskWithSingleParamAction extends DefaultTask {
         @TaskAction
-        public void doStuff(int value) {
+        public void doStuff(int value1) {
+        }
+    }
+
+    public static class TaskWithMultiParamAction extends DefaultTask {
+        @TaskAction
+        public void doStuff(int value1, int value2) {
         }
     }
 
@@ -777,6 +841,19 @@ public class AnnotationProcessingTaskFactoryTest {
         }
     }
 
+    public static class TaskWithBooleanInput extends DefaultTask {
+        boolean inputValue;
+
+        public TaskWithBooleanInput(boolean inputValue) {
+            this.inputValue = inputValue;
+        }
+
+        @Input
+        public boolean isInputValue() {
+            return inputValue;
+        }
+    }
+
     public static class BrokenTaskWithInputDir extends TaskWithInputDir {
         public BrokenTaskWithInputDir(File inputDir) {
             super(inputDir);
diff --git a/subprojects/core/src/test/groovy/org/gradle/api/internal/project/taskfactory/TaskFactoryTest.groovy b/subprojects/core/src/test/groovy/org/gradle/api/internal/project/taskfactory/TaskFactoryTest.groovy
index eb83288..f5517b7 100644
--- a/subprojects/core/src/test/groovy/org/gradle/api/internal/project/taskfactory/TaskFactoryTest.groovy
+++ b/subprojects/core/src/test/groovy/org/gradle/api/internal/project/taskfactory/TaskFactoryTest.groovy
@@ -25,13 +25,13 @@ import org.gradle.api.internal.project.ProjectInternal
 import org.gradle.api.tasks.TaskInstantiationException
 import org.gradle.internal.reflect.Instantiator
 import org.gradle.internal.reflect.ObjectInstantiationException
-import org.gradle.util.HelperUtil
+import org.gradle.util.TestUtil
 import spock.lang.Specification
 
 class TaskFactoryTest extends Specification {
     final ClassGenerator generator = Mock()
     final Instantiator instantiator = Mock()
-    final ProjectInternal project = HelperUtil.createRootProject()
+    final ProjectInternal project = TestUtil.createRootProject()
     final ITaskFactory taskFactory = new TaskFactory(generator).createChild(project, instantiator)
 
     def setup() {
@@ -94,6 +94,23 @@ class TaskFactoryTest extends Specification {
         task.dependsOn == ["/path1"] as Set
     }
 
+    public void taskCreationFailsWithUnknownArguments() {
+        when:
+        taskFactory.createTask([name: 'task', dependson: 'anotherTask'])
+
+        then:
+        InvalidUserDataException exception = thrown()
+        exception.message == "Could not create task 'task': Unknown argument(s) in task definition: [dependson]"
+
+        when:
+        taskFactory.createTask([name: 'task', Type: NotATask])
+
+        then:
+        exception = thrown()
+        exception.message == "Could not create task 'task': Unknown argument(s) in task definition: [Type]"
+
+    }
+
     public void testCreateTaskWithAction() {
         Action<Task> action = Mock()
 
@@ -118,7 +135,7 @@ class TaskFactoryTest extends Specification {
 
     public void testCreateTaskForTypeWhichDoesNotImplementTask() {
         when:
-        taskFactory.createTask([name: 'task', type:  NotATask])
+        taskFactory.createTask([name: 'task', type: NotATask])
 
         then:
         InvalidUserDataException e = thrown()
@@ -129,7 +146,7 @@ class TaskFactoryTest extends Specification {
         def failure = new RuntimeException()
 
         when:
-        taskFactory.createTask([name: 'task', type:  TestDefaultTask])
+        taskFactory.createTask([name: 'task', type: TestDefaultTask])
 
         then:
         TaskInstantiationException e = thrown()
diff --git a/subprojects/core/src/test/groovy/org/gradle/api/internal/resource/UriResourceTest.groovy b/subprojects/core/src/test/groovy/org/gradle/api/internal/resource/UriResourceTest.groovy
index dc2fb84..beb8119 100644
--- a/subprojects/core/src/test/groovy/org/gradle/api/internal/resource/UriResourceTest.groovy
+++ b/subprojects/core/src/test/groovy/org/gradle/api/internal/resource/UriResourceTest.groovy
@@ -19,7 +19,8 @@ package org.gradle.api.internal.resource
 
 import org.gradle.test.fixtures.file.TestFile
 import org.gradle.test.fixtures.file.TestNameTestDirectoryProvider
-import org.gradle.testing.internal.util.Network
+import org.gradle.util.TestPrecondition
+import org.junit.Assume
 import org.junit.Before
 import org.junit.Rule
 import org.junit.Test
@@ -150,7 +151,7 @@ class UriResourceTest {
 
     @Test
     public void hasNoContentWhenUsingHttpUriAndFileDoesNotExist() {
-        if (Network.offline) { return } // when this test moves to spock, ignore this test instead of just passing.
+        Assume.assumeTrue(TestPrecondition.ONLINE.fulfilled) // when this test moves to spock, ignore this test instead of just passing.
 
         UriResource resource = new UriResource('<display-name>', new URI("http://www.gradle.org/unknown.txt"));
         assertFalse(resource.exists)
diff --git a/subprojects/core/src/test/groovy/org/gradle/api/internal/resources/URIBuilderTest.groovy b/subprojects/core/src/test/groovy/org/gradle/api/internal/resources/URIBuilderTest.groovy
index cb11589..1cdb045 100644
--- a/subprojects/core/src/test/groovy/org/gradle/api/internal/resources/URIBuilderTest.groovy
+++ b/subprojects/core/src/test/groovy/org/gradle/api/internal/resources/URIBuilderTest.groovy
@@ -19,9 +19,6 @@ package org.gradle.api.internal.resources;
 
 import spock.lang.Specification
 
-/**
- * by Szczepan Faber, created at: 12/13/11
- */
 public class URIBuilderTest extends Specification {
 
     def "builds URIs"() {
diff --git a/subprojects/core/src/test/groovy/org/gradle/api/internal/tasks/DefaultTaskContainerTest.groovy b/subprojects/core/src/test/groovy/org/gradle/api/internal/tasks/DefaultTaskContainerTest.groovy
index d1ca77b..9a6689a 100644
--- a/subprojects/core/src/test/groovy/org/gradle/api/internal/tasks/DefaultTaskContainerTest.groovy
+++ b/subprojects/core/src/test/groovy/org/gradle/api/internal/tasks/DefaultTaskContainerTest.groovy
@@ -14,13 +14,9 @@
  * limitations under the License.
  */
 
-package org.gradle.api.internal.tasks;
+package org.gradle.api.internal.tasks
 
-
-import org.gradle.api.InvalidUserDataException
-import org.gradle.api.Rule
-import org.gradle.api.Task
-import org.gradle.api.UnknownTaskException
+import org.gradle.api.*
 import org.gradle.api.internal.TaskInternal
 import org.gradle.api.internal.project.ProjectInternal
 import org.gradle.api.internal.project.taskfactory.ITaskFactory
@@ -28,7 +24,6 @@ import org.gradle.api.tasks.TaskDependency
 import org.gradle.initialization.ProjectAccessListener
 import org.gradle.internal.reflect.Instantiator
 import org.gradle.util.GUtil
-import org.gradle.util.HelperUtil
 import spock.lang.Specification
 
 import static java.util.Collections.singletonMap
@@ -76,7 +71,7 @@ public class DefaultTaskContainerTest extends Specification {
 
     void "adds by name and closure"() {
         given:
-        final Closure action = HelperUtil.toClosure("{ description = 'description' }")
+        final Closure action = {}
         def options = singletonMap(Task.TASK_NAME, "task")
         def task = task("task")
 
@@ -90,6 +85,38 @@ public class DefaultTaskContainerTest extends Specification {
         1 * task.configure(action) >> task
     }
 
+    void "creates by name and closure"() {
+        given:
+        final Closure action = {}
+        def options = singletonMap(Task.TASK_NAME, "task")
+        def task = task("task")
+
+        taskFactory.createTask(options) >> task
+
+        when:
+        def added = container.create("task", action)
+
+        then:
+        added == task
+        1 * task.configure(action) >> task
+    }
+
+    void "creates by name and action"() {
+        given:
+        def action = Mock(Action)
+        def options = singletonMap(Task.TASK_NAME, "task")
+        def task = task("task")
+
+        taskFactory.createTask(options) >> task
+
+        when:
+        def added = container.create("task", action)
+
+        then:
+        added == task
+        1 * action.execute(task)
+    }
+
     void "replaces task by name"() {
         given:
         def options = singletonMap(Task.TASK_NAME, "task")
@@ -272,13 +299,43 @@ public class DefaultTaskContainerTest extends Specification {
         aTaskDependency.getDependencies(task) >> { container.add("b"); Collections.singleton(b) }
         task.dependsOn("b")
 
+        addPlaceholderTask("c")
+
         assert container.size() == 1
 
         when:
         container.actualize()
 
         then:
-        container.size() == 2
+        container.size() == 3
+    }
+
+    void "can add task via placeholder action"() {
+        when:
+        addPlaceholderTask("task")
+        then:
+        container.getByName("task") != null
+    }
+
+    void "task priotized over placeholders"() {
+        given:
+        Task task = addTask("task")
+        Runnable placeholderAction = addPlaceholderTask("task")
+
+        when:
+        container.getByName("task") == task
+
+        then:
+        0 * placeholderAction.run()
+    }
+
+    void "getNames contains task and placeholder action names"() {
+        when:
+        addTask("task1")
+        Runnable placeholderAction = addPlaceholderTask("task2")
+        0 * placeholderAction.run()
+        then:
+        container.names ==  ['task1', 'task2'] as SortedSet
     }
 
     private ProjectInternal expectTaskLookupInOtherProject(final String projectPath, final String taskName, def task) {
@@ -299,11 +356,18 @@ public class DefaultTaskContainerTest extends Specification {
         }
     }
 
+    private Runnable addPlaceholderTask(String placeholderName) {
+        Runnable runnable = Mock(Runnable)
+        runnable.run() >> { addTask(placeholderName) }
+        container.addPlaceholderAction(placeholderName, runnable)
+        runnable
+    }
+
     private Task addTask(String name) {
         def task = task(name)
         def options = singletonMap(Task.TASK_NAME, name)
         taskFactory.createTask(options) >> task
-        container.add(name)
+        container.create(name)
         return task;
     }
 }
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
index f7bf87e..725b307 100644
--- 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
@@ -1,5 +1,5 @@
 /*
- * Copyright 2010 the original author or authors.
+ * Copyright 2013 the original author or authors.
  *
  * Licensed under the Apache License, Version 2.0 (the "License");
  * you may not use this file except in compliance with the License.
@@ -15,11 +15,12 @@
  */
 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.ContextAwareTaskAction;
+import org.gradle.api.internal.tasks.TaskExecutionContext;
 import org.gradle.api.internal.tasks.TaskStateInternal;
 import org.gradle.api.tasks.StopActionException;
 import org.gradle.api.tasks.StopExecutionException;
@@ -47,11 +48,10 @@ import static org.junit.Assert.assertThat;
 public class ExecuteActionsTaskExecuterTest {
     private final JUnit4Mockery context = new JUnit4GroovyMockery();
     private final TaskInternal task = context.mock(TaskInternal.class, "<task>");
-    @SuppressWarnings("unchecked")
-    private final Action<Task> action1 = context.mock(Action.class, "action1");
-    @SuppressWarnings("unchecked")
-    private final Action<Task> action2 = context.mock(Action.class, "action2");
+    private final ContextAwareTaskAction action1 = context.mock(ContextAwareTaskAction.class, "action1");
+    private final ContextAwareTaskAction action2 = context.mock(ContextAwareTaskAction.class, "action2");
     private final TaskStateInternal state = context.mock(TaskStateInternal.class);
+    private final TaskExecutionContext executionContext = context.mock(TaskExecutionContext.class);
     private final ScriptSource scriptSource = context.mock(ScriptSource.class);
     private final StandardOutputCapture standardOutputCapture = context.mock(StandardOutputCapture.class);
     private final Sequence sequence = context.sequence("seq");
@@ -79,7 +79,7 @@ public class ExecuteActionsTaskExecuterTest {
     @Test
     public void doesNothingWhenTaskHasNoActions() {
         context.checking(new Expectations() {{
-            allowing(task).getActions();
+            allowing(task).getTaskActions();
             will(returnValue(emptyList()));
 
             one(listener).beforeActions(task);
@@ -98,13 +98,13 @@ public class ExecuteActionsTaskExecuterTest {
             inSequence(sequence);
         }});
 
-        executer.execute(task, state);
+        executer.execute(task, state, executionContext);
     }
 
     @Test
     public void executesEachActionInOrder() {
         context.checking(new Expectations() {{
-            allowing(task).getActions();
+            allowing(task).getTaskActions();
             will(returnValue(toList(action1, action2)));
 
             one(listener).beforeActions(task);
@@ -119,9 +119,15 @@ public class ExecuteActionsTaskExecuterTest {
             one(standardOutputCapture).start();
             inSequence(sequence);
 
+            one(action1).contextualise(executionContext);
+            inSequence(sequence);
+
             one(action1).execute(task);
             inSequence(sequence);
 
+            one(action1).contextualise(null);
+            inSequence(sequence);
+
             one(standardOutputCapture).stop();
             inSequence(sequence);
 
@@ -131,9 +137,15 @@ public class ExecuteActionsTaskExecuterTest {
             one(standardOutputCapture).start();
             inSequence(sequence);
 
+            one(action2).contextualise(executionContext);
+            inSequence(sequence);
+
             one(action2).execute(task);
             inSequence(sequence);
 
+            one(action2).contextualise(null);
+            inSequence(sequence);
+
             one(standardOutputCapture).stop();
             inSequence(sequence);
 
@@ -147,7 +159,7 @@ public class ExecuteActionsTaskExecuterTest {
             inSequence(sequence);
         }});
 
-        executer.execute(task, state);
+        executer.execute(task, state, executionContext);
     }
 
     @Test
@@ -157,6 +169,9 @@ public class ExecuteActionsTaskExecuterTest {
                 allowing(task).getActions();
                 will(returnValue(toList(action1)));
 
+                allowing(task).getTaskActions();
+                will(returnValue(toList(action1)));
+
                 one(listener).beforeActions(task);
                 inSequence(sequence);
 
@@ -169,6 +184,9 @@ public class ExecuteActionsTaskExecuterTest {
                 one(standardOutputCapture).start();
                 inSequence(sequence);
 
+                one(action1).contextualise(executionContext);
+                inSequence(sequence);
+
                 one(action1).execute(task);
                 will(new CustomAction("Add action to actions list") {
                     public Object invoke(Invocation invocation) throws Throwable {
@@ -179,6 +197,9 @@ public class ExecuteActionsTaskExecuterTest {
 
                 inSequence(sequence);
 
+                one(action1).contextualise(null);
+                inSequence(sequence);
+
                 one(standardOutputCapture).stop();
                 one(state).executed(null);
                 inSequence(sequence);
@@ -190,7 +211,7 @@ public class ExecuteActionsTaskExecuterTest {
                 inSequence(sequence);
             }
         });
-        executer.execute(task, state);
+        executer.execute(task, state, executionContext);
     }
 
 
@@ -199,7 +220,7 @@ public class ExecuteActionsTaskExecuterTest {
         final Throwable failure = new RuntimeException("failure");
         final Collector<Throwable> wrappedFailure = collector();
         context.checking(new Expectations() {{
-            allowing(task).getActions();
+            allowing(task).getTaskActions();
             will(returnValue(toList(action1, action2)));
 
             one(listener).beforeActions(task);
@@ -214,10 +235,16 @@ public class ExecuteActionsTaskExecuterTest {
             one(standardOutputCapture).start();
             inSequence(sequence);
 
+            one(action1).contextualise(executionContext);
+            inSequence(sequence);
+
             one(action1).execute(task);
             will(throwException(failure));
             inSequence(sequence);
 
+            one(action1).contextualise(null);
+            inSequence(sequence);
+
             one(standardOutputCapture).stop();
             inSequence(sequence);
 
@@ -232,7 +259,7 @@ public class ExecuteActionsTaskExecuterTest {
             inSequence(sequence);
         }});
 
-        executer.execute(task, state);
+        executer.execute(task, state, executionContext);
 
         assertThat(wrappedFailure.get(), instanceOf(TaskExecutionException.class));
         TaskExecutionException exception = (TaskExecutionException) wrappedFailure.get();
@@ -244,7 +271,7 @@ public class ExecuteActionsTaskExecuterTest {
     @Test
     public void stopsAtFirstActionWhichThrowsStopExecutionException() {
         context.checking(new Expectations() {{
-            allowing(task).getActions();
+            allowing(task).getTaskActions();
             will(returnValue(toList(action1, action2)));
 
             one(listener).beforeActions(task);
@@ -259,10 +286,16 @@ public class ExecuteActionsTaskExecuterTest {
             one(standardOutputCapture).start();
             inSequence(sequence);
 
+            one(action1).contextualise(executionContext);
+            inSequence(sequence);
+
             one(action1).execute(task);
             will(throwException(new StopExecutionException("stop")));
             inSequence(sequence);
 
+            one(action1).contextualise(null);
+            inSequence(sequence);
+
             one(standardOutputCapture).stop();
             inSequence(sequence);
 
@@ -276,13 +309,13 @@ public class ExecuteActionsTaskExecuterTest {
             inSequence(sequence);
         }});
 
-        executer.execute(task, state);
+        executer.execute(task, state, executionContext);
     }
 
     @Test
     public void skipsActionWhichThrowsStopActionException() {
         context.checking(new Expectations() {{
-            allowing(task).getActions();
+            allowing(task).getTaskActions();
             will(returnValue(toList(action1, action2)));
 
             one(listener).beforeActions(task);
@@ -297,10 +330,16 @@ public class ExecuteActionsTaskExecuterTest {
             one(standardOutputCapture).start();
             inSequence(sequence);
 
+            one(action1).contextualise(executionContext);
+            inSequence(sequence);
+
             one(action1).execute(task);
             will(throwException(new StopActionException("stop")));
             inSequence(sequence);
 
+            one(action1).contextualise(null);
+            inSequence(sequence);
+
             one(standardOutputCapture).stop();
             inSequence(sequence);
 
@@ -310,9 +349,15 @@ public class ExecuteActionsTaskExecuterTest {
             one(standardOutputCapture).start();
             inSequence(sequence);
 
+            one(action2).contextualise(executionContext);
+            inSequence(sequence);
+
             one(action2).execute(task);
             inSequence(sequence);
 
+            one(action2).contextualise(null);
+            inSequence(sequence);
+
             one(standardOutputCapture).stop();
             inSequence(sequence);
 
@@ -326,6 +371,6 @@ public class ExecuteActionsTaskExecuterTest {
             inSequence(sequence);
         }});
 
-        executer.execute(task, state);
+        executer.execute(task, state, executionContext);
     }
 }
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
index ecb7cb2..d751c10 100644
--- 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
@@ -16,14 +16,17 @@
 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.TaskExecutionContext
+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
-import org.gradle.api.internal.tasks.TaskExecuter
-import org.gradle.api.internal.tasks.TaskStateInternal
-import static org.junit.Assert.*
-import static org.hamcrest.Matchers.*
+
+import static org.hamcrest.Matchers.sameInstance
+import static org.junit.Assert.assertThat
+import static org.junit.Assert.fail
 
 @RunWith(JMock.class)
 class ExecuteAtMostOnceTaskExecuterTest {
@@ -31,6 +34,7 @@ class ExecuteAtMostOnceTaskExecuterTest {
     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 TaskExecutionContext executionContext = context.mock(TaskExecutionContext)
     private final ExecuteAtMostOnceTaskExecuter executer = new ExecuteAtMostOnceTaskExecuter(target)
 
     @Test
@@ -40,7 +44,7 @@ class ExecuteAtMostOnceTaskExecuterTest {
             will(returnValue(true))
         }
 
-        executer.execute(task, state)
+        executer.execute(task, state, executionContext)
     }
 
     @Test
@@ -48,11 +52,11 @@ class ExecuteAtMostOnceTaskExecuterTest {
         context.checking {
             allowing(state).getExecuted()
             will(returnValue(false))
-            one(target).execute(task, state)
+            one(target).execute(task, state, executionContext)
             one(state).executed()
         }
 
-        executer.execute(task, state)
+        executer.execute(task, state, executionContext)
     }
 
     @Test
@@ -62,13 +66,13 @@ class ExecuteAtMostOnceTaskExecuterTest {
         context.checking {
             allowing(state).getExecuted()
             will(returnValue(false))
-            one(target).execute(task, state)
+            one(target).execute(task, state, executionContext)
             will(throwException(failure))
             one(state).executed()
         }
 
         try {
-            executer.execute(task, state)
+            executer.execute(task, state, executionContext)
             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
index 9054502..aa17273 100644
--- 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
@@ -14,52 +14,38 @@
  * 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.TaskExecutionContext
 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)
+import spock.lang.Specification
+
+class PostExecutionAnalysisTaskExecuterTest extends Specification {
+    def target = Mock(TaskExecuter)
+    def task = Mock(TaskInternal)
+    def state = Mock(TaskStateInternal)
+    def context = Mock(TaskExecutionContext)
+    final PostExecutionAnalysisTaskExecuter executer = new PostExecutionAnalysisTaskExecuter(target)
+
+    def marksTaskUpToDateWhenItHasActionsAndItDidNotDoWork() {
+        when:
+        executer.execute(task, state, context)
+
+        then:
+        1 * target.execute(task, state, context)
+        1 * state.didWork >> false
+        1 * state.upToDate()
+        0 * _
     }
 
-    @Test
-    public void doesNotMarkTaskUpToDateWhenItHasActionsAndDidWork() {
-        context.checking {
-            one(target).execute(task, state)
-            allowing(task).getActions();
-            will(returnValue([{} as Action]))
-            allowing(state).getDidWork()
-            will(returnValue(true))
-        }
+    def doesNotMarkTaskUpToDateWhenItHasActionsAndDidWork() {
+        when:
+        executer.execute(task, state, context)
 
-        executer.execute(task, state)
+        then:
+        1 * target.execute(task, state, context)
+        1 * state.didWork >> true
+        0 * _
     }
 }
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
index c6746c0..8a638b8 100644
--- 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
@@ -18,6 +18,7 @@ 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.TaskExecutionContext
 import org.gradle.api.internal.tasks.TaskStateInternal
 import org.gradle.api.tasks.TaskInputs
 import spock.lang.Specification
@@ -26,6 +27,7 @@ class SkipEmptySourceFilesTaskExecuterTest extends Specification {
     final TaskExecuter target = Mock()
     final TaskInternal task = Mock()
     final TaskStateInternal state = Mock()
+    final TaskExecutionContext executionContext = Mock()
     final TaskInputs taskInputs = Mock()
     final FileCollection sourceFiles = Mock()
     final SkipEmptySourceFilesTaskExecuter executer = new SkipEmptySourceFilesTaskExecuter(target)
@@ -41,7 +43,7 @@ class SkipEmptySourceFilesTaskExecuterTest extends Specification {
         sourceFiles.empty >> true
 
         when:
-        executer.execute(task, state)
+        executer.execute(task, state, executionContext)
 
         then:
         1 * state.upToDate()
@@ -55,10 +57,10 @@ class SkipEmptySourceFilesTaskExecuterTest extends Specification {
         sourceFiles.empty >> false
 
         when:
-        executer.execute(task, state)
+        executer.execute(task, state, executionContext)
 
         then:
-        1 * target.execute(task, state)
+        1 * target.execute(task, state, executionContext)
         0 * target._
         0 * state._
     }
@@ -68,10 +70,10 @@ class SkipEmptySourceFilesTaskExecuterTest extends Specification {
         taskInputs.hasSourceFiles >> false
 
         when:
-        executer.execute(task, state)
+        executer.execute(task, state, executionContext)
 
         then:
-        1 * target.execute(task, state)
+        1 * target.execute(task, state, executionContext)
         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
index 80ab5cf..21453f0 100644
--- 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
@@ -21,6 +21,7 @@ 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.TaskExecutionContext;
 import org.gradle.api.internal.tasks.TaskStateInternal;
 import org.gradle.api.specs.Spec;
 import org.gradle.groovy.scripts.ScriptSource;
@@ -42,6 +43,8 @@ public class SkipOnlyIfTaskExecuterTest {
     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 TaskExecutionContext executionContext = context.mock(TaskExecutionContext.class);
+
     private final ScriptSource scriptSource = context.mock(ScriptSource.class);
     private final TaskExecuter delegate = context.mock(TaskExecuter.class);
     private final SkipOnlyIfTaskExecuter executer = new SkipOnlyIfTaskExecuter(delegate);
@@ -73,10 +76,10 @@ public class SkipOnlyIfTaskExecuterTest {
             allowing(spec).isSatisfiedBy(task);
             will(returnValue(true));
 
-            one(delegate).execute(task, state);
+            one(delegate).execute(task, state, executionContext);
         }});
 
-        executer.execute(task, state);
+        executer.execute(task, state, executionContext);
     }
 
     @Test
@@ -89,7 +92,7 @@ public class SkipOnlyIfTaskExecuterTest {
             one(state).skipped("SKIPPED");
         }});
 
-        executer.execute(task, state);
+        executer.execute(task, state, executionContext);
     }
 
     @Test
@@ -105,7 +108,7 @@ public class SkipOnlyIfTaskExecuterTest {
             will(collectTo(wrappedFailure));
         }});
 
-        executer.execute(task, state);
+        executer.execute(task, state, executionContext);
 
         GradleException exception = (GradleException) wrappedFailure.get();
         assertThat(exception.getMessage(), equalTo("Could not evaluate onlyIf predicate for <task>."));
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
index 429f7ea..e3dd720 100644
--- 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
@@ -16,15 +16,17 @@
 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.TaskExecutionContext
+import org.gradle.api.internal.tasks.TaskStateInternal
 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 TaskExecutionContext executionContext = Mock()
     final TaskExecuter target = Mock()
     final TaskInternal dependency = Mock()
     final TaskStateInternal dependencyState = Mock()
@@ -43,7 +45,7 @@ class SkipTaskWithNoActionsExecuterTest extends Specification {
         dependencyState.skipped >> true
 
         when:
-        executor.execute(task, state)
+        executor.execute(task, state, executionContext)
 
         then:
         1 * state.upToDate()
@@ -57,7 +59,7 @@ class SkipTaskWithNoActionsExecuterTest extends Specification {
         dependencyState.skipped >> false
 
         when:
-        executor.execute(task, state)
+        executor.execute(task, state, executionContext)
 
         then:
         0 * target._
@@ -69,10 +71,10 @@ class SkipTaskWithNoActionsExecuterTest extends Specification {
         task.actions >> [{} as TaskAction]
 
         when:
-        executor.execute(task, state)
+        executor.execute(task, state, executionContext)
 
         then:
-        1 * target.execute(task, state)
+        1 * target.execute(task, state, executionContext)
         0 * target._
         0 * state._
     }
diff --git a/subprojects/core/src/test/groovy/org/gradle/api/internal/tasks/execution/SkipUpToDateTaskExecuterTest.groovy b/subprojects/core/src/test/groovy/org/gradle/api/internal/tasks/execution/SkipUpToDateTaskExecuterTest.groovy
new file mode 100644
index 0000000..1d474dd
--- /dev/null
+++ b/subprojects/core/src/test/groovy/org/gradle/api/internal/tasks/execution/SkipUpToDateTaskExecuterTest.groovy
@@ -0,0 +1,110 @@
+/*
+ * Copyright 2010 the original author or authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.gradle.api.internal.tasks.execution
+
+import org.gradle.api.Action
+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.internal.changedetection.TaskArtifactState
+import org.gradle.api.internal.changedetection.TaskArtifactStateRepository
+import org.gradle.api.internal.tasks.TaskExecuter
+import org.gradle.api.internal.tasks.TaskExecutionContext
+import org.gradle.api.internal.tasks.TaskStateInternal
+import spock.lang.Specification
+
+public class SkipUpToDateTaskExecuterTest extends Specification {
+    def delegate = Mock(TaskExecuter)
+    def outputs = Mock(TaskOutputsInternal)
+    def task = Mock(TaskInternal)
+    def taskState = Mock(TaskStateInternal)
+    def taskContext = Mock(TaskExecutionContext)
+    def repository = Mock(TaskArtifactStateRepository)
+    def taskArtifactState = Mock(TaskArtifactState)
+    def executionHistory = Mock(TaskExecutionHistory)
+    Action<Task> action = Mock(Action)
+
+    def executer = new SkipUpToDateTaskExecuter(repository, delegate)
+
+    def skipsTaskWhenOutputsAreUpToDate() {
+        when:
+        executer.execute(task, taskState, taskContext);
+
+        then:
+        1 * repository.getStateFor(task) >> taskArtifactState
+        1 * taskArtifactState.isUpToDate([]) >> true
+        1 * taskState.upToDate()
+        1 * taskArtifactState.finished()
+        0 * _
+    }
+    
+    def executesTaskWhenOutputsAreNotUpToDate() {
+        when:
+        executer.execute(task, taskState, taskContext);
+
+        then:
+        1 * repository.getStateFor(task) >> taskArtifactState
+        1 * taskArtifactState.isUpToDate([]) >> false
+
+        then:
+        1 * taskArtifactState.beforeTask()
+        1 * taskArtifactState.getExecutionHistory() >> executionHistory
+        1 * task.outputs >> outputs
+        1 * outputs.setHistory(executionHistory)
+        1 * taskContext.setTaskArtifactState(taskArtifactState)
+
+        then:
+        1 * delegate.execute(task, taskState, taskContext)
+        _ * taskState.getFailure() >> null
+
+        then:
+        1 * taskArtifactState.afterTask()
+        1 * task.outputs >> outputs
+        1 * outputs.setHistory(null)
+        1 * taskContext.setTaskArtifactState(null)
+        1 * taskArtifactState.finished()
+        0 * _
+    }
+
+    def doesNotUpdateStateWhenTaskFails() {
+        when:
+        executer.execute(task, taskState, taskContext)
+
+        then:
+        1 * repository.getStateFor(task) >> taskArtifactState
+        1 * taskArtifactState.isUpToDate([]) >> false
+
+        then:
+        1 * taskArtifactState.beforeTask()
+        1 * taskArtifactState.getExecutionHistory() >> executionHistory
+        1 * task.outputs >> outputs
+        1 * outputs.setHistory(executionHistory)
+        1 * taskContext.setTaskArtifactState(taskArtifactState)
+
+        then:
+        1 * delegate.execute(task, taskState, taskContext)
+        1 * taskState.getFailure() >> new RuntimeException()
+
+        then:
+        1 * task.outputs >> outputs
+        1 * outputs.setHistory(null)
+        1 * taskContext.setTaskArtifactState(null)
+        1 * taskArtifactState.finished()
+        0 * _
+    }
+}
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
deleted file mode 100644
index 61a5847..0000000
--- a/subprojects/core/src/test/groovy/org/gradle/api/internal/tasks/execution/SkipUpToDateTaskExecuterTest.java
+++ /dev/null
@@ -1,141 +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.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
index 74e5113..07c4a05 100644
--- 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
@@ -15,34 +15,36 @@
  */
 package org.gradle.api.internal.tasks.execution
 
-import spock.lang.Specification
-import org.gradle.api.internal.tasks.TaskStateInternal
+import org.gradle.api.InvalidUserDataException
 import org.gradle.api.internal.TaskInternal
 import org.gradle.api.internal.tasks.TaskExecuter
-import org.gradle.api.InvalidUserDataException
+import org.gradle.api.internal.tasks.TaskExecutionContext
+import org.gradle.api.internal.tasks.TaskStateInternal
 import org.gradle.api.tasks.TaskValidationException
+import spock.lang.Specification
 
 class ValidatingTaskExecuterTest extends Specification {
     final TaskExecuter target = Mock()
     final TaskInternal task = Mock()
     final TaskStateInternal state = Mock()
+    final TaskExecutionContext executionContext = Mock()
     final TaskValidator validator = Mock()
     final ValidatingTaskExecuter executer = new ValidatingTaskExecuter(target)
 
     def executesTaskWhenThereAreNoViolations() {
         when:
-        executer.execute(task, state)
+        executer.execute(task, state, executionContext)
 
         then:
         _ * task.validators >> [validator]
         1 * validator.validate(task, !null)
-        1 * target.execute(task, state)
+        1 * target.execute(task, state, executionContext)
         0 * _._
     }
 
     def failsTaskWhenThereIsAViolation() {
         when:
-        executer.execute(task, state)
+        executer.execute(task, state, executionContext)
 
         then:
         _ * task.validators >> [validator]
@@ -59,7 +61,7 @@ class ValidatingTaskExecuterTest extends Specification {
 
     def failsTaskWhenThereAreMultipleViolations() {
         when:
-        executer.execute(task, state)
+        executer.execute(task, state, executionContext)
 
         then:
         _ * task.validators >> [validator]
diff --git a/subprojects/core/src/test/groovy/org/gradle/api/internal/tasks/util/DefaultJavaForkOptionsTest.groovy b/subprojects/core/src/test/groovy/org/gradle/api/internal/tasks/util/DefaultJavaForkOptionsTest.groovy
index abce164..9141221 100755
--- a/subprojects/core/src/test/groovy/org/gradle/api/internal/tasks/util/DefaultJavaForkOptionsTest.groovy
+++ b/subprojects/core/src/test/groovy/org/gradle/api/internal/tasks/util/DefaultJavaForkOptionsTest.groovy
@@ -17,10 +17,11 @@
 
 package org.gradle.api.internal.tasks.util
 
+import org.gradle.api.internal.file.TestFiles
+
 import java.nio.charset.Charset
 import org.gradle.api.file.FileCollection
 import org.gradle.api.internal.file.FileResolver
-import org.gradle.api.internal.file.IdentityFileResolver
 import org.gradle.process.JavaForkOptions
 import org.gradle.process.internal.DefaultJavaForkOptions
 import org.gradle.util.JUnit4GroovyMockery
@@ -235,7 +236,7 @@ public class DefaultJavaForkOptionsTest {
     @Test
     public void canAddToBootstrapClasspath() {
         def files = ['file1.jar', 'file2.jar'].collect { new File(it).canonicalFile }
-        options = new DefaultJavaForkOptions(new IdentityFileResolver());
+        options = new DefaultJavaForkOptions(TestFiles.resolver());
         options.bootstrapClasspath(files[0])
         options.bootstrapClasspath(files[1])
 
@@ -245,7 +246,7 @@ public class DefaultJavaForkOptionsTest {
     @Test
     public void allJvmArgsIncludeBootstrapClasspath() {
         def files = ['file1.jar', 'file2.jar'].collect { new File(it).canonicalFile }
-        options = new DefaultJavaForkOptions(new IdentityFileResolver());
+        options = new DefaultJavaForkOptions(TestFiles.resolver());
         options.bootstrapClasspath(files)
 
         context.checking {
@@ -259,7 +260,7 @@ public class DefaultJavaForkOptionsTest {
     @Test
     public void canSetBootstrapClasspathViaAllJvmArgs() {
         def files = ['file1.jar', 'file2.jar'].collect { new File(it).canonicalFile }
-        options = new DefaultJavaForkOptions(new IdentityFileResolver());
+        options = new DefaultJavaForkOptions(TestFiles.resolver());
         options.bootstrapClasspath(files[0])
 
         options.allJvmArgs = ['-Xbootclasspath:' + files[1]]
diff --git a/subprojects/core/src/test/groovy/org/gradle/api/internal/xml/SimpleXmlWriterSpec.groovy b/subprojects/core/src/test/groovy/org/gradle/api/internal/xml/SimpleXmlWriterSpec.groovy
index 38a67b4..6e22d70 100644
--- a/subprojects/core/src/test/groovy/org/gradle/api/internal/xml/SimpleXmlWriterSpec.groovy
+++ b/subprojects/core/src/test/groovy/org/gradle/api/internal/xml/SimpleXmlWriterSpec.groovy
@@ -16,14 +16,11 @@
 
 package org.gradle.api.internal.xml
 
+import org.gradle.util.TextUtil
 import spock.lang.Specification
 
 import javax.xml.parsers.DocumentBuilderFactory
-import org.gradle.util.TextUtil
 
-/**
- * by Szczepan Faber, created at: 12/3/12
- */
 class SimpleXmlWriterSpec extends Specification {
 
     private sw = new ByteArrayOutputStream()
diff --git a/subprojects/core/src/test/groovy/org/gradle/api/plugins/TestPluginConvention1.groovy b/subprojects/core/src/test/groovy/org/gradle/api/plugins/TestPluginConvention1.groovy
index c38c20f..0af5662 100644
--- a/subprojects/core/src/test/groovy/org/gradle/api/plugins/TestPluginConvention1.groovy
+++ b/subprojects/core/src/test/groovy/org/gradle/api/plugins/TestPluginConvention1.groovy
@@ -16,9 +16,6 @@
  
 package org.gradle.api.plugins
 
-/**
- * @author Hans Dockter
- */
 class TestPluginConvention1 {
     String a = 'a1'
     String b = 'b'
diff --git a/subprojects/core/src/test/groovy/org/gradle/api/plugins/TestPluginConvention2.groovy b/subprojects/core/src/test/groovy/org/gradle/api/plugins/TestPluginConvention2.groovy
index a7e1b4d..0aab5b1 100644
--- a/subprojects/core/src/test/groovy/org/gradle/api/plugins/TestPluginConvention2.groovy
+++ b/subprojects/core/src/test/groovy/org/gradle/api/plugins/TestPluginConvention2.groovy
@@ -16,9 +16,6 @@
  
 package org.gradle.api.plugins
 
-/**
- * @author Hans Dockter
- */
 class TestPluginConvention2 {
     String a = 'a2'
 
diff --git a/subprojects/core/src/test/groovy/org/gradle/api/tasks/AbstractCopyTaskTest.groovy b/subprojects/core/src/test/groovy/org/gradle/api/tasks/AbstractCopyTaskTest.groovy
new file mode 100644
index 0000000..ad7f4a8
--- /dev/null
+++ b/subprojects/core/src/test/groovy/org/gradle/api/tasks/AbstractCopyTaskTest.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.api.tasks
+
+import org.gradle.api.file.FileCollection
+import org.gradle.api.file.FileTree
+import org.gradle.api.internal.file.copy.CopyAction
+import org.gradle.test.fixtures.file.WorkspaceTest
+import org.gradle.util.TestUtil
+import org.junit.Test
+
+class AbstractCopyTaskTest extends WorkspaceTest {
+
+    TestCopyTask task
+
+    def setup() {
+        task = TestUtil.createTask(TestCopyTask)
+    }
+
+    void usesDefaultSourceWhenNoSourceHasBeenSpecified() {
+        given:
+        def defaultSource = Mock(FileTree)
+
+        when:
+        task.defaultSource = defaultSource
+
+        then:
+        task.source.is(defaultSource)
+    }
+
+    public void doesNotUseDefaultSourceWhenSourceHasBeenSpecifiedOnSpec() {
+        when:
+        FileTree source = Mock(FileTree)
+        task.defaultSource = source
+        task.from "foo"
+
+        then:
+        !task.source.is(source)
+    }
+
+    @Test
+    public void copySpecMethodsDelegateToMainSpecOfCopyAction() {
+        given:
+        file("include") << "bar"
+
+        expect:
+        task.rootSpec.source.isEmpty()
+
+        when:
+        task.from testDirectory.absolutePath
+        task.include "include"
+
+        then:
+        task.mainSpec.getIncludes() == ["include"].toSet()
+        task.mainSpec.source.files == task.project.fileTree(testDirectory).files
+    }
+
+    static class TestCopyTask extends AbstractCopyTask {
+        CopyAction copyAction
+        FileCollection defaultSource
+
+        protected CopyAction createCopyAction() {
+            copyAction
+        }
+
+    }
+}
diff --git a/subprojects/core/src/test/groovy/org/gradle/api/tasks/AbstractCopyTaskTest.java b/subprojects/core/src/test/groovy/org/gradle/api/tasks/AbstractCopyTaskTest.java
deleted file mode 100644
index 61a7581..0000000
--- a/subprojects/core/src/test/groovy/org/gradle/api/tasks/AbstractCopyTaskTest.java
+++ /dev/null
@@ -1,99 +0,0 @@
-/*
- * Copyright 2009 the original author or authors.
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- *      http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-package org.gradle.api.tasks;
-
-import org.gradle.api.file.FileCollection;
-import org.gradle.api.file.FileTree;
-import org.gradle.api.internal.AbstractTask;
-import org.gradle.api.internal.file.copy.CopyActionImpl;
-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.hamcrest.Matchers.sameInstance;
-import static org.junit.Assert.assertThat;
-
- at RunWith(JMock.class)
-public class AbstractCopyTaskTest extends AbstractTaskTest {
-    private final JUnit4Mockery context = new JUnit4GroovyMockery();
-    private TestCopyTask task;
-
-    @Before
-    public void setUp() {
-        task = createTask(TestCopyTask.class);
-        task.action = context.mock(CopyActionImpl.class);
-        task.defaultSource = context.mock(FileCollection.class);
-    }
-
-    @Override
-    public AbstractTask getTask() {
-        return task;
-    }
-
-    @Test
-    public void usesDefaultSourceWhenNoSourceHasBeenSpecified() {
-        context.checking(new Expectations() {{
-            one(task.action).hasSource();
-            will(returnValue(false));
-
-        }});
-        assertThat(task.getSource(), sameInstance(task.defaultSource));
-    }
-
-    @Test
-    public void doesNotUseDefaultSourceWhenSourceHasBeenSpecifiedOnSpec() {
-        final FileTree source = context.mock(FileTree.class, "source");
-        context.checking(new Expectations() {{
-            one(task.action).hasSource();
-            will(returnValue(true));
-            one(task.action).getAllSource();
-            will(returnValue(source));
-        }});
-        assertThat(task.getSource(), sameInstance((FileCollection) source));
-    }
-
-
-    @Test
-    public void copySpecMethodsDelegateToMainSpecOfCopyAction() {
-        context.checking(new Expectations() {{
-            one(task.action).include("include");
-            one(task.action).from("source");
-        }});
-
-        assertThat(task.include("include"), sameInstance((AbstractCopyTask) task));
-        assertThat(task.from("source"), sameInstance((AbstractCopyTask) task));
-    }
-
-    public static class TestCopyTask extends AbstractCopyTask {
-        CopyActionImpl action;
-        FileCollection defaultSource;
-
-        @Override
-        protected CopyActionImpl getCopyAction() {
-            return action;
-        }
-
-        @Override
-        @SuppressWarnings("deprecation")
-        public FileCollection getDefaultSource() {
-            return defaultSource;
-        }
-    }
-}
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 9ac42c6..6c0f48c 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,5 +1,5 @@
 /*
- * Copyright 2009 the original author or authors.
+ * Copyright 2013 the original author or authors.
  *
  * Licensed under the Apache License, Version 2.0 (the "License");
  * you may not use this file except in compliance with the License.
@@ -13,69 +13,14 @@
  * 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() {
-        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()
-    }
+package org.gradle.api.tasks
 
-    @Test public void doesNotUseConventionValueForDestDirWhenSpecified() {
-        copyTask.conventionMapping.destinationDir = { new File('convention') }
+class CopyTest extends AbstractCopyTaskContractTest {
 
-        context.checking {
-            one(action).getDestinationDir()
-            will(returnValue(new File('dest')))
-            one(action).hasSource(); will(returnValue(true))
-        }
+    private Copy task = project.tasks.create(TEST_TASK_NAME, Copy)
 
-        copyTask.configureRootSpec()
+    AbstractCopyTask getTask() {
+        return task
     }
 }
diff --git a/subprojects/core/src/test/groovy/org/gradle/api/tasks/DeleteTest.java b/subprojects/core/src/test/groovy/org/gradle/api/tasks/DeleteTest.java
index 45cc0d1..b8c8558 100644
--- a/subprojects/core/src/test/groovy/org/gradle/api/tasks/DeleteTest.java
+++ b/subprojects/core/src/test/groovy/org/gradle/api/tasks/DeleteTest.java
@@ -35,9 +35,6 @@ import java.io.IOException;
 import static org.hamcrest.Matchers.equalTo;
 import static org.junit.Assert.*;
 
-/**
- * @author Hans Dockter
- */
 @RunWith(org.jmock.integration.junit4.JMock.class)
 public class DeleteTest extends AbstractConventionTaskTest {
     private Mockery context = new JUnit4GroovyMockery();
diff --git a/subprojects/core/src/test/groovy/org/gradle/api/tasks/DirectoryTest.groovy b/subprojects/core/src/test/groovy/org/gradle/api/tasks/DirectoryTest.groovy
index 9633666..f8e3842 100644
--- a/subprojects/core/src/test/groovy/org/gradle/api/tasks/DirectoryTest.groovy
+++ b/subprojects/core/src/test/groovy/org/gradle/api/tasks/DirectoryTest.groovy
@@ -16,18 +16,16 @@
 
 package org.gradle.api.tasks
 
+import org.gradle.api.GradleException
 import org.gradle.api.InvalidUserDataException
 import org.gradle.api.UncheckedIOException
 import org.gradle.api.internal.AbstractTask
 import org.junit.Before
 import org.junit.Test
+
+import static org.hamcrest.Matchers.instanceOf
 import static org.junit.Assert.*
-import static org.hamcrest.Matchers.*
-import org.gradle.api.GradleException
 
-/**
- * @author Hans Dockter
- */
 class DirectoryTest extends AbstractTaskTest {
     static final String TASK_DIR_NAME = 'parent/child'
     Directory directoryForAbstractTest
diff --git a/subprojects/core/src/test/groovy/org/gradle/api/tasks/GradleBuildTest.groovy b/subprojects/core/src/test/groovy/org/gradle/api/tasks/GradleBuildTest.groovy
index 2e3f6e6..676313a 100755
--- a/subprojects/core/src/test/groovy/org/gradle/api/tasks/GradleBuildTest.groovy
+++ b/subprojects/core/src/test/groovy/org/gradle/api/tasks/GradleBuildTest.groovy
@@ -18,54 +18,39 @@ package org.gradle.api.tasks
 import org.gradle.BuildResult
 import org.gradle.GradleLauncher
 import org.gradle.initialization.GradleLauncherFactory
-import org.gradle.api.internal.AbstractTask
-import org.junit.After
-import org.junit.Before
-import org.junit.Test
-import static org.hamcrest.Matchers.*
-import static org.junit.Assert.*
+import org.gradle.util.TestUtil
+import spock.lang.Specification
 
-public class GradleBuildTest extends AbstractTaskTest {
-    GradleLauncherFactory launcherFactoryMock = context.mock(GradleLauncherFactory.class)
-    GradleBuild task
+public class GradleBuildTest extends Specification {
+    GradleLauncherFactory launcherFactory = Mock()
+    GradleBuild task = TestUtil.createTask(GradleBuild, [gradleLauncherFactory: launcherFactory])
 
-    AbstractTask getTask() {
-        return task
-    }
-
-    @Before
-    void setUp() {
-        task = createTask(GradleBuild.class)
-        GradleLauncher.injectCustomFactory(launcherFactoryMock)
-    }
+    void usesCopyOfCurrentBuildsStartParams() {
+        def expectedStartParameter = task.project.gradle.startParameter.newBuild()
+        expectedStartParameter.currentDir = task.project.projectDir
 
-    @After
-    void tearDown() {
-        GradleLauncher.injectCustomFactory(null)
-    }
+        expect:
+        task.startParameter == expectedStartParameter
 
-    @Test
-    void usesCopyOfCurrentBuildsStartParams() {
-        def expectedStartParameter = project.gradle.startParameter.newBuild()
-        expectedStartParameter.currentDir = project.projectDir
-        assertThat(task.startParameter, equalTo(expectedStartParameter))
+        when:
         task.tasks = ['a', 'b']
-        assertThat(task.tasks, equalTo(['a', 'b']))
-        assertThat(task.startParameter.taskNames, equalTo(['a', 'b']))
+
+        then:
+        task.tasks == ['a', 'b']
+        task.startParameter.taskNames == ['a', 'b']
     }
 
-    @Test
     void executesBuild() {
-        GradleLauncher launcherMock = context.mock(GradleLauncher.class)
-        BuildResult resultMock = context.mock(BuildResult.class)
+        GradleLauncher launcher = Mock()
+        BuildResult resultMock = Mock()
 
-        context.checking {
-            one(launcherFactoryMock).newInstance(task.startParameter)
-            will(returnValue(launcherMock))
-            one(launcherMock).run()
-            will(returnValue(resultMock))
-            one(resultMock).rethrowFailure()
-        }
+        when:
         task.build()
+
+        then:
+        1 * launcherFactory.newInstance(task.startParameter) >> launcher
+        1 * launcher.run() >> resultMock
+        1 * resultMock.rethrowFailure()
+        0 * _._
     }
 }
diff --git a/subprojects/core/src/test/groovy/org/gradle/api/tasks/SyncTest.groovy b/subprojects/core/src/test/groovy/org/gradle/api/tasks/SyncTest.groovy
new file mode 100644
index 0000000..818c133
--- /dev/null
+++ b/subprojects/core/src/test/groovy/org/gradle/api/tasks/SyncTest.groovy
@@ -0,0 +1,27 @@
+/*
+ * Copyright 2013 the original author or authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF 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
+
+class SyncTest extends AbstractCopyTaskContractTest {
+
+    private Sync task = project.tasks.create(TEST_TASK_NAME, Sync)
+
+    @Override
+    AbstractCopyTask getTask() {
+        task
+    }
+}
diff --git a/subprojects/core/src/test/groovy/org/gradle/api/tasks/UploadTest.groovy b/subprojects/core/src/test/groovy/org/gradle/api/tasks/UploadTest.groovy
index 8b70973..af4e4cd 100644
--- a/subprojects/core/src/test/groovy/org/gradle/api/tasks/UploadTest.groovy
+++ b/subprojects/core/src/test/groovy/org/gradle/api/tasks/UploadTest.groovy
@@ -16,14 +16,14 @@
 
 package org.gradle.api.tasks
 
-import org.gradle.util.HelperUtil
+import org.gradle.util.TestUtil
 import spock.lang.Specification
 
 class UploadTest extends Specification {
 
     def "can create task"() {
         when:
-        HelperUtil.createTask(Upload)
+        TestUtil.createTask(Upload)
 
         then:
         noExceptionThrown()
diff --git a/subprojects/core/src/test/groovy/org/gradle/api/tasks/ant/AntTargetTest.java b/subprojects/core/src/test/groovy/org/gradle/api/tasks/ant/AntTargetTest.java
index 124125a..685ccfa 100644
--- a/subprojects/core/src/test/groovy/org/gradle/api/tasks/ant/AntTargetTest.java
+++ b/subprojects/core/src/test/groovy/org/gradle/api/tasks/ant/AntTargetTest.java
@@ -21,7 +21,7 @@ import org.apache.tools.ant.Target;
 import org.gradle.api.Task;
 import org.gradle.api.internal.project.DefaultProject;
 import org.gradle.test.fixtures.file.TestNameTestDirectoryProvider;
-import org.gradle.util.HelperUtil;
+import org.gradle.util.TestUtil;
 import org.junit.Before;
 import org.junit.Rule;
 import org.junit.Test;
@@ -36,8 +36,8 @@ import static org.junit.Assert.assertTrue;
 
 public class AntTargetTest {
     private final Target antTarget = new Target();
-    private final DefaultProject project = HelperUtil.createRootProject();
-    private final AntTarget task = HelperUtil.createTask(AntTarget.class, project);
+    private final DefaultProject project = TestUtil.createRootProject();
+    private final AntTarget task = TestUtil.createTask(AntTarget.class, project);
     @Rule
     public TestNameTestDirectoryProvider testDir = new TestNameTestDirectoryProvider();
     private final File baseDir = testDir.getTestDirectory();
@@ -62,8 +62,8 @@ public class AntTargetTest {
 
     @Test
     public void dependsOnTargetDependencies() {
-        Task a = project.getTasks().add("a");
-        Task b = project.getTasks().add("b");
+        Task a = project.getTasks().create("a");
+        Task b = project.getTasks().create("b");
         antTarget.setDepends("a, b");
 
         task.setTarget(antTarget);
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 c65aef8..4934138 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
@@ -16,14 +16,12 @@
 
 package org.gradle.api.tasks.bundling
 
-import static org.junit.Assert.*
-import static org.hamcrest.Matchers.*
 import org.junit.Before
-import org.junit.Test;
+import org.junit.Test
+
+import static org.hamcrest.Matchers.equalTo
+import static org.junit.Assert.assertThat;
 
-/**
- * @author Hans Dockter
- */
 class TarTest extends AbstractArchiveTaskTest {
     Tar tar
 
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 9ea31f0..e48bfaf 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
@@ -18,11 +18,9 @@ package org.gradle.api.tasks.bundling
 
 import org.junit.Before
 import org.junit.Test
-import static org.junit.Assert.*
 
-/**
- * @author Hans Dockter
- */
+import static org.junit.Assert.assertEquals
+
 class ZipTest extends AbstractArchiveTaskTest {
     Zip zip
 
diff --git a/subprojects/core/src/test/groovy/org/gradle/api/tasks/util/AbstractTestForPatternSet.groovy b/subprojects/core/src/test/groovy/org/gradle/api/tasks/util/AbstractTestForPatternSet.groovy
index e2480a6..6165667 100644
--- a/subprojects/core/src/test/groovy/org/gradle/api/tasks/util/AbstractTestForPatternSet.groovy
+++ b/subprojects/core/src/test/groovy/org/gradle/api/tasks/util/AbstractTestForPatternSet.groovy
@@ -16,15 +16,15 @@
 
 package org.gradle.api.tasks.util
 
-import static org.junit.Assert.*
-import static org.hamcrest.Matchers.*
-import static org.gradle.util.Matchers.*
 import org.junit.Before
 import org.junit.Test
 
-/**
- * @author Hans Dockter
- */
+import static org.gradle.util.Matchers.isEmpty
+import static org.hamcrest.Matchers.equalTo
+import static org.hamcrest.Matchers.sameInstance
+import static org.junit.Assert.assertEquals
+import static org.junit.Assert.assertThat
+
 abstract class AbstractTestForPatternSet {
     static final String TEST_PATTERN_1 = 'pattern1'
     static final String TEST_PATTERN_2 = 'pattern2'
diff --git a/subprojects/core/src/test/groovy/org/gradle/cache/internal/DefaultCacheAccessTest.groovy b/subprojects/core/src/test/groovy/org/gradle/cache/internal/DefaultCacheAccessTest.groovy
index fa4ffc9..b2b4210 100644
--- a/subprojects/core/src/test/groovy/org/gradle/cache/internal/DefaultCacheAccessTest.groovy
+++ b/subprojects/core/src/test/groovy/org/gradle/cache/internal/DefaultCacheAccessTest.groovy
@@ -16,6 +16,7 @@
 package org.gradle.cache.internal
 
 import org.gradle.cache.internal.btree.BTreePersistentIndexedCache
+import org.gradle.cache.internal.cacheops.CacheAccessOperationsStack
 import org.gradle.internal.Factory
 import org.gradle.messaging.serialize.Serializer
 import org.gradle.test.fixtures.file.TestNameTestDirectoryProvider
@@ -28,415 +29,321 @@ class DefaultCacheAccessTest extends Specification {
     @Rule final TestNameTestDirectoryProvider tmpDir = new TestNameTestDirectoryProvider()
     final FileLockManager lockManager = Mock()
     final File lockFile = tmpDir.file('lock.bin')
-    final File targetFile = tmpDir.file('cache.bin')
     final FileLock lock = Mock()
+    final CacheAccessOperationsStack operations = Mock()
     final BTreePersistentIndexedCache<String, Integer> backingCache = Mock()
-    final DefaultCacheAccess manager = new DefaultCacheAccess("<display-name>", lockFile, lockManager) {
-        @Override
-        def <K, V> BTreePersistentIndexedCache<K, V> doCreateCache(File cacheFile, Serializer<K> keySerializer, Serializer<V> valueSerializer) {
-            return backingCache
+    DefaultCacheAccess access = newAccess(operations)
+
+    private DefaultCacheAccess newAccess(CacheAccessOperationsStack operations) {
+        new DefaultCacheAccess("<display-name>", lockFile, lockManager, operations) {
+            @Override
+            def <K, V> BTreePersistentIndexedCache<K, V> doCreateCache(File cacheFile, Serializer<K> keySerializer, Serializer<V> valueSerializer) {
+                return backingCache
+            }
         }
     }
 
     def "acquires lock on open and releases on close when initial lock mode is not none"() {
         when:
-        manager.open(Shared)
+        access.open(Shared)
 
         then:
         1 * lockManager.lock(lockFile, Shared, "<display-name>") >> lock
+        1 * lockManager.allowContention(lock, _ as Runnable)
+        1 * operations.pushCacheAction("Access <display-name>")
         0 * _._
 
+        and:
+        access.owner
+
         when:
-        manager.close()
+        access.close()
 
         then:
         1 * lock.close()
+        1 * operations.close()
         0 * _._
+
+        and:
+        !access.owner
     }
 
-    def "does not acquires lock on open when initial lock mode is none"() {
+    def "lock cannot be acquired more than once when initial lock mode is not none"() {
+        lockManager.lock(lockFile, Shared, "<display-name>") >> lock
+
         when:
-        manager.open(None)
+        access.open(Shared)
+        access.open(Shared)
 
         then:
-        0 * _._
+        thrown(IllegalStateException)
+    }
 
+    def "does not acquire lock on open when initial lock mode is none"() {
         when:
-        manager.close()
+        access.open(None)
 
         then:
         0 * _._
-    }
-
-    def "acquires lock at the start of the cache action and releases lock at the end of the cache action when initial lock mode is none"() {
-        Factory<String> action = Mock()
-
-        given:
-        manager.open(None)
 
         when:
-        manager.useCache("some operation", action)
+        access.close()
 
         then:
-        1 * lockManager.lock(lockFile, Exclusive, "<display-name>", "some operation") >> lock
-
-        and:
-        1 * action.create()
+        1 * operations.close()
+        0 * _._
 
         and:
-        1 * lock.close()
-        0 * _._
+        !access.owner
     }
 
-    def "does not acquire lock at start of cache action when initial lock mode is exclusive"() {
+    def "using cache pushes an operation and acquires ownership"() {
         Factory<String> action = Mock()
 
-        given:
-        1 * lockManager.lock(lockFile, Exclusive, "<display-name>") >> lock
-        manager.open(Exclusive)
-        def cache = manager.newCache(targetFile, String, Integer)
-
         when:
-        manager.useCache("some operation", action)
+        access.useCache("some operation", action)
+
+        then:
+        1 * operations.pushCacheAction("some operation")
+
+        then:
+        1 * operations.description >> "some operation"
+        1 * lockManager.lock(lockFile, Exclusive, "<display-name>", "some operation") >> lock
+        1 * lockManager.allowContention(lock, _ as Runnable)
 
         then:
         1 * action.create() >> {
-            canAccess cache
+            assert access.owner
         }
-        _ * lock.readFile(_)
-        _ * lock.writeFile(_)
 
-        and:
+        then:
+        1 * lock.getMode() >> Exclusive
+        1 * operations.inCacheAction >> false
+        1 * operations.popCacheAction("some operation")
         0 * _._
+
+        and:
+        !access.owner
     }
 
-    def "can create cache instance outside of cache action"() {
-        given:
-        manager.open(None)
+    def "nested use cache operation does not release the ownership"() {
+        Factory<String> action = Mock()
 
         when:
-        def cache = manager.newCache(tmpDir.file('cache.bin'), String.class, Integer.class)
+        access.useCache("some operation", action)
 
         then:
-        cache instanceof MultiProcessSafePersistentIndexedCache
-        0 * _._
-    }
+        1 * lockManager.lock(lockFile, Exclusive, "<display-name>", _) >> lock
+        1 * action.create()
+        1 * operations.inCacheAction >> true
 
-    def "can create cache instance inside of cache action"() {
-        def cache
+        then:
+        access.owner
+    }
 
-        given:
-        manager.open(None)
+    def "use cache operation reuses existing file lock"() {
+        Factory<String> action = Mock()
 
         when:
-        manager.useCache("init", {
-            cache = manager.newCache(tmpDir.file('cache.bin'), String.class, Integer.class)
-        } as Factory)
+        access.open(Exclusive)
 
         then:
-        cache instanceof MultiProcessSafePersistentIndexedCache
+        1 * lockManager.lock(lockFile, Exclusive, "<display-name>") >> lock
 
-        and:
-        1 * lockManager.lock(lockFile, Exclusive, _, _) >> lock
-    }
+        when:
+        access.useCache("some operation", action)
 
-    def "can use cache instance during cache action"() {
-        Factory<String> action = Mock()
+        then:
+        0 * lockManager._
+        1 * action.create()
+    }
 
-        given:
-        manager.open(None)
-        def cache = manager.newCache(targetFile, String, Integer)
+    def "use cache operation does not allow shared locks"() {
+        access.open(Shared)
 
         when:
-        manager.useCache("some operation", action)
+        access.useCache("some operation", Mock(Factory))
 
         then:
-        1 * action.create() >> {
-            canAccess cache
-        }
-        1 * lockManager.lock(lockFile, Exclusive, "<display-name>", "some operation") >> lock
-        _ * lock.readFile(_)
-
-        and:
-        _ * lock.writeFile(_)
-        1 * lock.close()
-        0 * _._
+        thrown(UnsupportedOperationException)
     }
 
-    def "releases lock before long running operation and reacquires after"() {
-        Factory<String> action = Mock()
-        Factory<String> longRunningAction = Mock()
-
-        given:
-        manager.open(None)
-        def cache = manager.newCache(targetFile, String, Integer)
-
+    def "long running operation fails early when there is no lock"() {
         when:
-        manager.useCache("some operation", action)
+        access.longRunningOperation("some operation", Mock(Factory))
 
         then:
-        1 * action.create() >> {
-            canAccess cache
-            manager.longRunningOperation("nested", longRunningAction)
-            canAccess cache
-        }
-        1 * longRunningAction.create()
-        2 * lockManager.lock(lockFile, Exclusive, "<display-name>", "some operation") >> lock
-        _ * lock.readFile(_)
-        _ * lock.writeFile(_)
-        2 * lock.close()
-        0 * _._
+        IllegalStateException e = thrown()
+        e.message == 'Cannot start long running operation, as the <display-name> has not been locked.'
     }
 
-    def "releases lock before nested long running operation and reacquires after"() {
+    def "long running operation pushes an operation and releases ownership"() {
+        lock.mode >> Exclusive
         Factory<String> action = Mock()
-        Factory<String> lockInsideLongRunningOperation = Mock()
-        Factory<String> nestedLongRunningAction = Mock()
-        Factory<String> deeplyNestedAction = Mock()
 
-        FileLock anotherLock = Mock()
+        when:
+        access.open(Exclusive)
 
-        given:
-        manager.open(None)
-        def cache = manager.newCache(targetFile, String, Integer)
+        then:
+        1 * lockManager.lock(lockFile, Exclusive, "<display-name>") >> lock
+        assert access.owner
 
         when:
-        manager.useCache("some operation", action)
+        access.longRunningOperation("some operation", action)
+
+        then:
+        1 * operations.maybeReentrantLongRunningOperation("some operation") >> false
+
+        then:
+        0 * lock.close()
+        1 * operations.pushLongRunningOperation("some operation")
 
         then:
         1 * action.create() >> {
-            canAccess cache
-            manager.longRunningOperation("nested", lockInsideLongRunningOperation)
-            canAccess cache
-        }
-        1 * lockInsideLongRunningOperation.create() >> {
-            cannotAccess cache
-            manager.useCache("nested operation", nestedLongRunningAction)
-            cannotAccess cache
-        }
-        1 * nestedLongRunningAction.create() >> {
-            canAccess cache
-            manager.longRunningOperation("nested-2", deeplyNestedAction)
-            canAccess cache
-        }
-        1 * deeplyNestedAction.create() >> {
-            cannotAccess cache
+            assert !access.owner
         }
 
-        2 * lockManager.lock(lockFile, Exclusive, "<display-name>", "some operation") >> lock
-        _ * lock.readFile(_)
-        _ * lock.writeFile(_)
-        2 * lock.close()
+        then:
+        0 * lockManager._
+        1 * operations.popLongRunningOperation("some operation")
 
-        2 * lockManager.lock(lockFile, Exclusive, "<display-name>", "nested operation") >> anotherLock
-        _ * anotherLock.readFile(_)
-        _ * anotherLock.writeFile(_)
-        2 * anotherLock.close()
-        0 * _._
+        then:
+        access.owner
     }
 
-    def "cannot run long running operation from outside cache action"() {
-        given:
-        manager.open(None)
+    def "long running operation closes the lock if contended"() {
+        Factory<String> action = Mock()
 
         when:
-        manager.longRunningOperation("operation", Mock(Factory))
+        access.open(Exclusive)
 
         then:
-        IllegalStateException e = thrown()
-        e.message == 'Cannot start long running operation, as the <display-name> has not been locked.'
-    }
+        1 * lockManager.lock(lockFile, Exclusive, "<display-name>") >> lock
 
-    def "cannot use cache from within long running operation"() {
-        Factory<String> action = Mock()
-        Factory<String> longRunningAction = Mock()
+        when:
+        access.whenContended().run()
+        access.longRunningOperation("some operation", action)
 
-        given:
-        manager.open(None)
-        def cache = manager.newCache(targetFile, String, Integer)
+        then:
+        1 * lock.close()
 
-        when:
-        manager.useCache("some operation", action)
+        then:
+        1 * action.create()
 
         then:
-        _ * lockManager.lock(lockFile, Exclusive, "<display-name>", "some operation") >> lock
-        1 * action.create() >> {
-            manager.longRunningOperation("nested", longRunningAction)
-        }
-        1 * longRunningAction.create() >> {
-            cannotAccess cache
-        }
+        1 * lockManager.lock(lockFile, Exclusive, "<display-name>", _)
     }
 
-    def "can execute cache action from within long running operation"() {
+    def "long running operation closes the lock if the lock is shared"() {
         Factory<String> action = Mock()
-        Factory<String> longRunningAction = Mock()
-        Factory<String> nestedAction = Mock()
-
-        given:
-        manager.open(None)
-        def cache = manager.newCache(targetFile, String, Integer)
 
         when:
-        manager.useCache("some operation", action)
+        access.open(Shared)
 
         then:
-        1 * lockManager.lock(lockFile, Exclusive, "<display-name>", "some operation") >> lock
+        1 * lockManager.lock(lockFile, Shared, "<display-name>") >> lock
 
-        and:
-        1 * action.create() >> {
-            canAccess cache
-            manager.longRunningOperation("nested", longRunningAction)
-            canAccess cache
-        }
+        when:
+        access.longRunningOperation("some operation", action)
 
-        and:
+        then:
+        1 * lock.mode >> Shared
         1 * lock.close()
 
-        and:
-        1 * longRunningAction.create() >> {
-            cannotAccess cache
-            manager.useCache("nested 2", nestedAction)
-            cannotAccess cache
-        }
-
-        and:
-        1 * lockManager.lock(lockFile, Exclusive, "<display-name>", "nested 2") >> lock
-
-        and:
-        1 * nestedAction.create() >> {
-            canAccess cache
-        }
+        then:
+        1 * action.create()
 
-        and:
-        1 * lockManager.lock(lockFile, Exclusive, "<display-name>", "some operation") >> lock
-        _ * lock.readFile(_)
-        _ * lock.writeFile(_)
-        2 * lock.close()
-        0 * _._
+        then:
+        1 * lockManager.lock(lockFile, Exclusive, "<display-name>", _)
     }
 
-    def "can execute long running operation from within long running operation"() {
+    def "reentrant long running operation does not involve locking"() {
         Factory<String> action = Mock()
-        Factory<String> longRunningAction = Mock()
-        Factory<String> nestedAction = Mock()
-
-        given:
-        manager.open(None)
-        def cache = manager.newCache(targetFile, String, Integer)
 
         when:
-        manager.useCache("some operation", action)
+        access.longRunningOperation("some operation", action)
 
         then:
-        1 * action.create() >> {
-            canAccess cache
-            manager.longRunningOperation("nested", longRunningAction)
-            canAccess cache
-        }
-        1 * longRunningAction.create() >> {
-            cannotAccess cache
-            manager.longRunningOperation("nested 2", nestedAction)
-            cannotAccess cache
-        }
-        1 * nestedAction.create() >> {
-            cannotAccess cache
-        }
-        2 * lockManager.lock(lockFile, Exclusive, "<display-name>", "some operation") >> lock
-        _ * lock.readFile(_)
-        _ * lock.writeFile(_)
-        2 * lock.close()
-        0 * _._
+        1 * operations.maybeReentrantLongRunningOperation("some operation") >> true
+
+        then:
+        1 * action.create()
+
+        then:
+        1 * operations.popLongRunningOperation("some operation")
+        0 * lock._
+        0 * lockManager._
     }
 
-    def "can execute cache action from within cache action"() {
-        Factory<String> action = Mock()
-        Factory<String> nestedAction = Mock()
+    def "can create new cache"() {
+        when:
+        def cache = access.newCache(tmpDir.file('cache.bin'), String.class, Integer.class)
 
-        given:
-        manager.open(None)
-        def cache = manager.newCache(targetFile, String, Integer)
+        then:
+        cache instanceof MultiProcessSafePersistentIndexedCache
+        0 * _._
+    }
 
+    def "contended action does nothing when no lock"() {
         when:
-        manager.useCache("some operation", action)
+        access.whenContended().run()
 
         then:
-        1 * action.create() >> {
-            canAccess cache
-            manager.useCache("nested", nestedAction)
-            canAccess cache
-        }
-        1 * nestedAction.create() >> {
-            canAccess cache
-        }
-        1 * lockManager.lock(lockFile, Exclusive, "<display-name>", "some operation") >> lock
-        _ * lock.readFile(_)
-        _ * lock.writeFile(_)
-        1 * lock.close()
         0 * _._
     }
 
-    def "closes caches at the end of the cache action when initial lock mode is none"() {
+    def "contended action safely closes the lock when cache is not busy"() {
         Factory<String> action = Mock()
 
-        given:
-        manager.open(None)
-        def cache = manager.newCache(targetFile, String, Integer)
-
         when:
-        manager.useCache("some operation", action)
+        access.open(Exclusive)
+        access.longRunningOperation("some operation", action)
 
         then:
-        1 * action.create() >> {
-            canAccess cache
+        1 * lockManager.lock(lockFile, Exclusive, "<display-name>") >> lock
+        action.create() >> {
+            access.whenContended().run()
         }
-        1 * lockManager.lock(lockFile, Exclusive, "<display-name>", "some operation") >> lock
-        _ * lock.readFile(_)
 
         and:
-        _ * lock.writeFile(_) >> {Runnable runnable -> runnable.run()}
-        1 * backingCache.close()
+        1 * operations.pushCacheAction('Other process requested access to <display-name>')
         1 * lock.close()
-        0 * _._
+        1 * operations.popCacheAction('Other process requested access to <display-name>')
     }
 
-    def "closes caches on close when initial lock mode is not none"() {
-        given:
-        1 * lockManager.lock(lockFile, Exclusive, "<display-name>") >> lock
-        _ * lock.readFile(_) >> {Factory factory -> factory.create()}
-        _ * lock.writeFile(_) >> {Runnable runnable -> runnable.run()}
-
-        and:
-        manager.open(Exclusive)
-        def cache = manager.newCache(targetFile, String, Integer)
-        cache.get("key")
+    def "file access requires acquired lock"() {
+        def runnable = Mock(Runnable)
 
         when:
-        manager.close()
+        access.open(None)
+        access.fileAccess.updateFile(runnable)
 
         then:
-        _ * lock.readFile(_) >> {Factory factory -> factory.create()}
-        _ * lock.writeFile(_) >> {Runnable runnable -> runnable.run()}
-        1 * backingCache.close()
-        1 * lock.close()
-        0 * _._
+        thrown(IllegalStateException)
     }
 
-    def canAccess(def cache) {
-        try {
-            cache.get("key")
-        } catch (IllegalStateException e) {
-            assert false: "Should be able to access cache here"
-        }
-    }
+    def "file access is available when lock is acquired"() {
+        def runnable = Mock(Runnable)
 
-    def cannotAccess(def cache) {
-        try {
-            cache.get("key")
-            assert false: "Should not be able to access cache here"
-        } catch (IllegalStateException e) {
-            assert e.message == 'The <display-name> has not been locked.'
-        }
+        when:
+        access.open(Exclusive)
+        access.fileAccess.updateFile(runnable)
+
+        then:
+        1 * lockManager.lock(lockFile, Exclusive, "<display-name>") >> lock
+        1 * lock.updateFile(runnable)
     }
 
-}
+    def "file access can be accessed when there is no owner"() {
+        def runnable = Mock(Runnable)
+
+        when:
+        access.open(None)
+        access.useCache("use cache", {} as Runnable) //acquires file lock but releases the thread lock
+        access.fileAccess.updateFile(runnable)
+
+        then:
+        1 * lockManager.lock(lockFile, Exclusive, "<display-name>", _) >> lock
+        1 * lock.updateFile(runnable)
+    }
+}
\ No newline at end of file
diff --git a/subprojects/core/src/test/groovy/org/gradle/cache/internal/DefaultCacheFactoryTest.groovy b/subprojects/core/src/test/groovy/org/gradle/cache/internal/DefaultCacheFactoryTest.groovy
index 3de6d72..29a8572 100755
--- a/subprojects/core/src/test/groovy/org/gradle/cache/internal/DefaultCacheFactoryTest.groovy
+++ b/subprojects/core/src/test/groovy/org/gradle/cache/internal/DefaultCacheFactoryTest.groovy
@@ -18,6 +18,7 @@ package org.gradle.cache.internal
 import org.gradle.CacheUsage
 import org.gradle.api.Action
 import org.gradle.cache.CacheValidator
+import org.gradle.cache.internal.locklistener.NoOpFileLockContentionHandler
 import org.gradle.messaging.serialize.DefaultSerializer
 import org.gradle.test.fixtures.file.TestNameTestDirectoryProvider
 import org.junit.Rule
@@ -29,7 +30,7 @@ class DefaultCacheFactoryTest extends Specification {
     final Action<?> opened = Mock()
     final Action<?> closed = Mock()
     final ProcessMetaDataProvider metaDataProvider = Mock()
-    private final DefaultCacheFactory factoryFactory = new DefaultCacheFactory(new DefaultFileLockManager(metaDataProvider)) {
+    private final DefaultCacheFactory factoryFactory = new DefaultCacheFactory(new DefaultFileLockManager(metaDataProvider, new NoOpFileLockContentionHandler())) {
         @Override
         void onOpen(Object cache) {
             opened.execute(cache)
diff --git a/subprojects/core/src/test/groovy/org/gradle/cache/internal/DefaultFileLockManagerTest.groovy b/subprojects/core/src/test/groovy/org/gradle/cache/internal/DefaultFileLockManagerTest.groovy
index f43bc06..d841feb 100644
--- a/subprojects/core/src/test/groovy/org/gradle/cache/internal/DefaultFileLockManagerTest.groovy
+++ b/subprojects/core/src/test/groovy/org/gradle/cache/internal/DefaultFileLockManagerTest.groovy
@@ -18,7 +18,9 @@ package org.gradle.cache.internal
 
 import org.apache.commons.lang.RandomStringUtils
 import org.gradle.cache.internal.FileLockManager.LockMode
+import org.gradle.cache.internal.locklistener.NoOpFileLockContentionHandler
 import org.gradle.internal.Factory
+import org.gradle.internal.id.IdGenerator
 import org.gradle.test.fixtures.file.TestFile
 import org.gradle.test.fixtures.file.TestNameTestDirectoryProvider
 import org.gradle.util.Requires
@@ -30,13 +32,12 @@ import spock.lang.Unroll
 import static org.gradle.cache.internal.FileLockManager.LockMode.Exclusive
 import static org.gradle.cache.internal.FileLockManager.LockMode.Shared
 
-/**
- * @author: Szczepan Faber, created at: 8/30/11
- */
 class DefaultFileLockManagerTest extends Specification {
     @Rule TestNameTestDirectoryProvider tmpDir = new TestNameTestDirectoryProvider()
     def metaDataProvider = Mock(ProcessMetaDataProvider)
-    FileLockManager manager = new DefaultFileLockManager(metaDataProvider)
+    def generator = Stub(IdGenerator)
+
+    FileLockManager manager = new DefaultFileLockManager(metaDataProvider, 5000, new NoOpFileLockContentionHandler(), generator)
 
     TestFile testFile
     TestFile testFileLock
@@ -51,6 +52,7 @@ class DefaultFileLockManagerTest extends Specification {
 
         metaDataProvider.processIdentifier >> '123'
         metaDataProvider.processDisplayName >> 'process'
+        generator.generateId() >> 678L
     }
 
     @Unroll
@@ -396,7 +398,7 @@ class DefaultFileLockManagerTest extends Specification {
         def customMetaDataProvider = Mock(ProcessMetaDataProvider)
         def processIdentifier = RandomStringUtils.randomAlphanumeric(1000)
         1 * customMetaDataProvider.processIdentifier >> processIdentifier
-        def customManager = new DefaultFileLockManager(customMetaDataProvider)
+        def customManager = new DefaultFileLockManager(customMetaDataProvider, 5000, new NoOpFileLockContentionHandler(), generator)
         def operationalDisplayName = RandomStringUtils.randomAlphanumeric(1000)
 
         when:
@@ -453,7 +455,9 @@ class DefaultFileLockManagerTest extends Specification {
         lockFile.withDataInputStream { str ->
             assert str.readByte() == 1
             assert !str.readBoolean()
-            assert str.readByte() == 2
+            assert str.readByte() == 3
+            assert str.readInt() == -1
+            assert str.readLong() == 678L
             assert str.readUTF() == processIdentifier
             assert str.readUTF() == operationalName
             assert str.read() < 0
diff --git a/subprojects/core/src/test/groovy/org/gradle/cache/internal/DefaultPersistentDirectoryCacheTest.java b/subprojects/core/src/test/groovy/org/gradle/cache/internal/DefaultPersistentDirectoryCacheTest.java
index fff1a16..268abc5 100644
--- a/subprojects/core/src/test/groovy/org/gradle/cache/internal/DefaultPersistentDirectoryCacheTest.java
+++ b/subprojects/core/src/test/groovy/org/gradle/cache/internal/DefaultPersistentDirectoryCacheTest.java
@@ -20,6 +20,7 @@ import org.gradle.api.Action;
 import org.gradle.cache.CacheOpenException;
 import org.gradle.cache.CacheValidator;
 import org.gradle.cache.PersistentCache;
+import org.gradle.cache.internal.locklistener.NoOpFileLockContentionHandler;
 import org.gradle.test.fixtures.file.TestFile;
 import org.gradle.test.fixtures.file.TestNameTestDirectoryProvider;
 import org.gradle.util.GUtil;
@@ -47,7 +48,7 @@ public class DefaultPersistentDirectoryCacheTest {
     public final TestNameTestDirectoryProvider tmpDir = new TestNameTestDirectoryProvider();
     private final JUnit4Mockery context = new JUnit4GroovyMockery();
     private final ProcessMetaDataProvider metaDataProvider = context.mock(ProcessMetaDataProvider.class);
-    private final FileLockManager lockManager = new DefaultFileLockManager(metaDataProvider);
+    private final FileLockManager lockManager = new DefaultFileLockManager(metaDataProvider, new NoOpFileLockContentionHandler());
     private final Action<PersistentCache> action = context.mock(Action.class);
     private final CacheValidator validator = context.mock(CacheValidator.class);
     private final Map<String, String> properties = GUtil.map("prop", "value", "prop2", "other-value");
diff --git a/subprojects/core/src/test/groovy/org/gradle/cache/internal/FileLockCommunicatorTest.groovy b/subprojects/core/src/test/groovy/org/gradle/cache/internal/FileLockCommunicatorTest.groovy
new file mode 100644
index 0000000..4cc108c
--- /dev/null
+++ b/subprojects/core/src/test/groovy/org/gradle/cache/internal/FileLockCommunicatorTest.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.cache.internal
+
+import org.gradle.messaging.remote.internal.inet.InetAddressFactory
+import org.gradle.util.ConcurrentSpecification
+
+import static org.gradle.test.fixtures.ConcurrentTestUtil.poll
+
+class FileLockCommunicatorTest extends ConcurrentSpecification {
+
+    def communicator = new FileLockCommunicator(new InetAddressFactory())
+    Long receivedId
+
+    def cleanup() {
+        communicator.stop()
+    }
+
+    def "knows port"() {
+        expect:
+        communicator.getPort() != -1
+    }
+
+    def "knows port after stopping"() {
+        when:
+        communicator.stop()
+
+        then:
+        communicator.getPort() == -1
+    }
+
+    def "can receive lock id"() {
+        start {
+            receivedId = communicator.receive()
+        }
+
+        poll {
+            assert communicator.getPort() != -1 && receivedId == null
+        }
+
+        when:
+        communicator.pingOwner(communicator.getPort(), 155, "lock")
+
+        then:
+        poll {
+            assert receivedId == 155
+        }
+    }
+
+    def "may not receive after the stop"() {
+        communicator.stop()
+        when:
+        communicator.receive()
+        then:
+        thrown(GracefullyStoppedException)
+    }
+
+    def "pinging on a port that nobody listens is safe"() {
+        when:
+        communicator.pingOwner(6666, 166, "lock")
+
+        then:
+        noExceptionThrown()
+    }
+
+    def "can be stopped"() {
+        expect:
+        communicator.stop()
+    }
+
+    def "can be stopped during receive"() {
+        start {
+            try {
+                communicator.receive()
+            } catch (GracefullyStoppedException e) {}
+        }
+
+        when:
+        communicator.stop()
+
+        then:
+        finished()
+    }
+}
diff --git a/subprojects/core/src/test/groovy/org/gradle/cache/internal/cacheops/CacheAccessOperationsStackTest.groovy b/subprojects/core/src/test/groovy/org/gradle/cache/internal/cacheops/CacheAccessOperationsStackTest.groovy
new file mode 100644
index 0000000..eea7bc3
--- /dev/null
+++ b/subprojects/core/src/test/groovy/org/gradle/cache/internal/cacheops/CacheAccessOperationsStackTest.groovy
@@ -0,0 +1,142 @@
+/*
+ * Copyright 2011 the original author or authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.gradle.cache.internal.cacheops
+
+import org.gradle.util.ConcurrentSpecification
+
+import java.util.concurrent.CountDownLatch
+import java.util.concurrent.TimeUnit
+
+class CacheAccessOperationsStackTest extends ConcurrentSpecification {
+
+    def stack = new CacheAccessOperationsStack()
+
+    def "maintains operations per thread"() {
+        expect:
+        start {
+            assert !stack.isInCacheAction()
+            stack.pushCacheAction("foo1")
+            stack.pushCacheAction("foo2")
+            assert stack.description == "foo2"
+        }
+        start {
+            assert !stack.isInCacheAction()
+            stack.pushCacheAction("bar1")
+            stack.pushCacheAction("bar2")
+            assert stack.description == "bar2"
+        }
+    }
+
+    def "cannot access operations from a different thread"() {
+        expect:
+        start {
+            stack.pushCacheAction("foo")
+        }
+        finished()
+
+        when:
+        start {
+            stack.popCacheAction("foo")
+        }
+        finished()
+
+        then:
+        thrown(IllegalStateException)
+    }
+
+    def "manages reentrant long running operations"() {
+        expect:
+        start {
+            assert !stack.maybeReentrantLongRunningOperation("long")
+        }
+        finished()
+        start {
+            assert !stack.maybeReentrantLongRunningOperation("long")
+            stack.pushLongRunningOperation("long")
+        }
+        finished()
+        start {
+            assert stack.maybeReentrantLongRunningOperation("long2")
+            assert stack.description == "long2"
+        }
+    }
+
+    def "when all long running operations complete the next operation is no longer reentrant"() {
+        when:
+        stack.pushLongRunningOperation("foo")
+        stack.pushLongRunningOperation("foo2")
+
+        then:
+        stack.maybeReentrantLongRunningOperation("foo3")
+
+        when:
+        stack.popLongRunningOperation("foo3")
+        stack.popLongRunningOperation("foo2")
+        stack.popLongRunningOperation("foo")
+
+        then:
+        !stack.maybeReentrantLongRunningOperation("hey")
+    }
+
+    def "if any thread is in cache the next long running operation is not reentrant"() {
+        when:
+        stack.pushLongRunningOperation("long")
+        start { stack.pushCacheAction("cache") }
+        finished()
+
+        then:
+        !stack.maybeReentrantLongRunningOperation("long2")
+    }
+
+    def "long running operation is reentrant if previous long running operation belongs to a different thread"() {
+        when:
+        start {
+            stack.pushLongRunningOperation("a")
+        }
+        finished()
+        then:
+        stack.maybeReentrantLongRunningOperation("b")
+    }
+
+    def "long running operations in separate threads can interleave"() {
+        when:
+        //Here's the scenario:
+        //Thread 1: pushes a
+        //Thread 2: pushes b
+        //Thread 1: pops a
+        //Thread 2: pops b
+
+        def latch1 = new CountDownLatch(1)
+        def latch2 = new CountDownLatch(1)
+        start {
+            stack.pushLongRunningOperation("a")
+            latch1.await(1, TimeUnit.SECONDS)
+            stack.popLongRunningOperation("a")
+            latch2.countDown()
+        }
+        start {
+            stack.pushLongRunningOperation("b")
+            latch1.countDown()
+            latch2.await(1, TimeUnit.SECONDS)
+            stack.popLongRunningOperation("b")
+        }
+        finished()
+
+        then:
+        noExceptionThrown()
+    }
+}
diff --git a/subprojects/core/src/test/groovy/org/gradle/cache/internal/cacheops/CacheOperationStackTest.groovy b/subprojects/core/src/test/groovy/org/gradle/cache/internal/cacheops/CacheOperationStackTest.groovy
new file mode 100644
index 0000000..7011814
--- /dev/null
+++ b/subprojects/core/src/test/groovy/org/gradle/cache/internal/cacheops/CacheOperationStackTest.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.cache.internal.cacheops
+
+import spock.lang.Specification
+
+class CacheOperationStackTest extends Specification {
+
+    def stack = new CacheOperationStack()
+
+    def "provides no description initially"() {
+        when:
+        stack.description
+        then:
+        thrown(IllegalStateException)
+    }
+
+    def "manages long running operations"() {
+        when:
+        stack.pushLongRunningOperation("long")
+        then:
+        stack.description == "long"
+
+        when:
+        stack.pushLongRunningOperation("long2")
+        then:
+        stack.description == "long2"
+
+        when:
+        stack.popLongRunningOperation("long2")
+        then:
+        stack.description == "long"
+
+        when:
+        stack.popLongRunningOperation("long")
+        and:
+        stack.description
+        then:
+        thrown(IllegalStateException)
+    }
+
+    def "manages cache actions"() {
+        when:
+        stack.pushCacheAction("foo")
+        then:
+        stack.description == "foo"
+
+        when:
+        stack.pushCacheAction("foo2")
+        then:
+        stack.description == "foo2"
+
+        when:
+        stack.popCacheAction("foo2")
+        then:
+        stack.description == "foo"
+
+        when:
+        stack.popCacheAction("foo")
+        and:
+        stack.description
+        then:
+        thrown(IllegalStateException)
+    }
+
+    def "prevents popping latest operation if the name does not match"() {
+        stack.pushCacheAction("foo")
+
+        when:
+        stack.popCacheAction("foo2")
+        then:
+        thrown(IllegalStateException)
+    }
+
+    def "prevents popping latest operation if the kind does not match"() {
+        stack.pushCacheAction("foo")
+
+        when:
+        stack.popLongRunningOperation("foo")
+        then:
+        thrown(IllegalStateException)
+    }
+
+    def "knows the kind of current cache operation"() {
+        assert !stack.isInCacheAction()
+
+        when:
+        stack.pushLongRunningOperation("long")
+        then:
+        !stack.inCacheAction
+        stack.inLongRunningOperation
+
+        when:
+        stack.pushCacheAction("cache")
+        then:
+        stack.inCacheAction
+        !stack.inLongRunningOperation
+
+        when:
+        stack.pushCacheAction("cache2")
+        then:
+        stack.inCacheAction
+        !stack.inLongRunningOperation
+
+        when:
+        stack.popCacheAction("cache2")
+        stack.popCacheAction("cache")
+        then:
+        !stack.inCacheAction
+        stack.inLongRunningOperation
+    }
+}
diff --git a/subprojects/core/src/test/groovy/org/gradle/cache/internal/locklistener/DefaultFileLockContentionHandlerTest.groovy b/subprojects/core/src/test/groovy/org/gradle/cache/internal/locklistener/DefaultFileLockContentionHandlerTest.groovy
new file mode 100644
index 0000000..3440b77
--- /dev/null
+++ b/subprojects/core/src/test/groovy/org/gradle/cache/internal/locklistener/DefaultFileLockContentionHandlerTest.groovy
@@ -0,0 +1,185 @@
+/*
+ * Copyright 2013 the original author or authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.gradle.cache.internal.locklistener
+
+import org.gradle.internal.concurrent.ExecutorFactory
+import org.gradle.internal.concurrent.StoppableExecutor
+import org.gradle.messaging.remote.internal.inet.InetAddressFactory
+import org.gradle.util.ConcurrentSpecification
+
+import java.util.concurrent.atomic.AtomicBoolean
+
+import static org.gradle.test.fixtures.ConcurrentTestUtil.poll
+
+class DefaultFileLockContentionHandlerTest extends ConcurrentSpecification {
+    def addressFactory = new InetAddressFactory()
+    def handler = new DefaultFileLockContentionHandler(executorFactory, addressFactory)
+    def client = new DefaultFileLockContentionHandler(executorFactory, addressFactory)
+
+    def cleanup() {
+        handler?.stop()
+        client?.stop()
+    }
+
+    def "manages contention for multiple locks"() {
+        def action1 = new AtomicBoolean()
+        def action2 = new AtomicBoolean()
+
+        when:
+        int port = handler.reservePort()
+        handler.start(10, { action1.set(true) })
+        handler.start(11, { action2.set(true) })
+
+        client.pingOwner(port, 10, "lock 1")
+        client.pingOwner(port, 11, "lock 2")
+
+        then:
+        poll {
+            assert action1.get() && action2.get()
+        }
+    }
+
+    def "there is only one executor thread"() {
+        def factory = Mock(ExecutorFactory)
+        handler = new DefaultFileLockContentionHandler(factory, addressFactory)
+
+        when:
+        handler.reservePort()
+        handler.start(10, {} as Runnable)
+        handler.start(11, {} as Runnable)
+
+        then:
+        1 * factory.create(_ as String) >> Mock(StoppableExecutor)
+        0 * factory._
+    }
+
+    def "cannot start contention handling when the handler was stopped"() {
+        handler.stop()
+
+        when:
+        handler.start(10, {} as Runnable)
+
+        then:
+        thrown(IllegalStateException)
+    }
+
+    def "cannot start contention handling when the handler was not initialized"() {
+        when:
+        handler.start(10, {} as Runnable)
+
+        then:
+        thrown(IllegalStateException)
+    }
+
+    def "handler can be closed and contended action does not run"() {
+        when:
+        int port = handler.reservePort();
+        handler.start(10, { throw new RuntimeException("Boo!") } as Runnable)
+        handler.stop()
+
+        client.pingOwner(port, 10, "lock 1")
+
+        then:
+        noExceptionThrown()
+    }
+
+    def "can receive request for lock that is already closed"() {
+        when:
+        int port = handler.reservePort()
+        handler.start(10, { assert false } as Runnable)
+        sleep(300) //so that it starts receiving
+
+        //close the lock
+        handler.stop(10)
+
+        //receive request for lock that is already closed
+        client.pingOwner(port, 10, "lock 1")
+
+        then:
+        canHandleMoreRequests()
+    }
+
+    private void canHandleMoreRequests() {
+        def executed = new AtomicBoolean()
+        int port = handler.reservePort();
+        handler.start(15, { executed.set(true) } as Runnable)
+        client.pingOwner(port, 15, "lock")
+        poll { assert executed.get() }
+    }
+
+    def "reserving port is safely reentrant"() {
+        when:
+        int port = handler.reservePort()
+
+        then:
+        handler.reservePort() == port
+    }
+
+    def "cannot reserve port when the handler was stopped"() {
+        handler.stop()
+
+        when:
+        handler.reservePort()
+
+        then:
+        thrown(IllegalStateException)
+    }
+
+    def "reserving port does not start the thread"() {
+        def factory = Mock(ExecutorFactory)
+        handler = new DefaultFileLockContentionHandler(factory, addressFactory)
+
+        when:
+        handler.reservePort()
+
+        then:
+        0 * factory._
+    }
+
+    def "stopping the handler stops the executor"() {
+        def factory = Mock(ExecutorFactory)
+        def executor = Mock(StoppableExecutor)
+        handler = new DefaultFileLockContentionHandler(factory, addressFactory)
+
+        when:
+        handler.reservePort()
+        handler.start(10, {} as Runnable)
+        handler.stop()
+
+        then:
+        1 * factory.create(_ as String) >> executor
+        1 * executor.stop()
+    }
+
+    def "stopping is safe even if the handler was not initialized"() {
+        when:
+        handler.stop()
+
+        then:
+        noExceptionThrown()
+    }
+
+    def "stopping is safe even if the executor was not initialized"() {
+        handler.reservePort()
+
+        when:
+        handler.stop()
+
+        then:
+        noExceptionThrown()
+    }
+}
diff --git a/subprojects/core/src/test/groovy/org/gradle/configuration/BuildScriptProcessorTest.java b/subprojects/core/src/test/groovy/org/gradle/configuration/BuildScriptProcessorTest.java
deleted file mode 100644
index 60ae726..0000000
--- a/subprojects/core/src/test/groovy/org/gradle/configuration/BuildScriptProcessorTest.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.configuration;
-
-import org.gradle.api.internal.project.ProjectInternal;
-import org.gradle.api.internal.project.ProjectStateInternal;
-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;
-
- at RunWith(JMock.class)
-public class BuildScriptProcessorTest {
-    private final JUnit4Mockery context = new JUnit4GroovyMockery();
-    private final ProjectInternal project = context.mock(ProjectInternal.class);
-    private final ScriptSource scriptSource = context.mock(ScriptSource.class);
-    private final ScriptPluginFactory configurerFactory = context.mock(ScriptPluginFactory.class);
-    private final ScriptPlugin scriptPlugin = context.mock(ScriptPlugin.class);
-    private final ProjectStateInternal state = context.mock(ProjectStateInternal.class);
-    private final BuildScriptProcessor evaluator = new BuildScriptProcessor(configurerFactory);
-
-    @Before
-    public void setUp() {
-        context.checking(new Expectations() {{
-            allowing(project).getBuildScriptSource();
-            will(returnValue(scriptSource));
-            ignoring(scriptSource);
-        }});
-    }
-
-    @Test
-    public void configuresProjectUsingBuildScript() {
-        context.checking(new Expectations() {{
-            one(configurerFactory).create(scriptSource);
-            will(returnValue(scriptPlugin));
-
-            one(scriptPlugin).apply(project);
-        }});
-
-        evaluator.evaluate(project, state);
-    }
-}
\ No newline at end of file
diff --git a/subprojects/core/src/test/groovy/org/gradle/configuration/DefaultBuildConfigurerTest.groovy b/subprojects/core/src/test/groovy/org/gradle/configuration/DefaultBuildConfigurerTest.groovy
index 347c903..7a7b531 100644
--- a/subprojects/core/src/test/groovy/org/gradle/configuration/DefaultBuildConfigurerTest.groovy
+++ b/subprojects/core/src/test/groovy/org/gradle/configuration/DefaultBuildConfigurerTest.groovy
@@ -32,16 +32,19 @@ class DefaultBuildConfigurerTest extends Specification {
     }
 
     def "configures build for standard mode"() {
+        def child1 = Mock(ProjectInternal)
+        def child2 = Mock(ProjectInternal)
+
+        given:
+        _ * rootProject.allprojects >> [rootProject, child1, child2]
+
         when:
         configurer.configure(gradle)
 
         then:
-        1 * gradle.addProjectEvaluationListener(_ as ImplicitTasksConfigurer);
-        1 * gradle.addProjectEvaluationListener(_ as ProjectDependencies2TaskResolver);
-        1 * rootProject.allprojects(!null) >> { args ->
-            assert args[0] instanceof DefaultBuildConfigurer.ConfigureProject
-        }
-        0 * rootProject._
+        1 * rootProject.evaluate()
+        1 * child1.evaluate()
+        1 * child2.evaluate()
     }
 
     def "configures build for on demand mode"() {
@@ -49,12 +52,8 @@ class DefaultBuildConfigurerTest extends Specification {
         configurer.configure(gradle)
 
         then:
-        1 * startParameter.isConfigureOnDemand() >> true
+        startParameter.isConfigureOnDemand() >> true
         1 * rootProject.evaluate()
         0 * rootProject._
-
-        and:
-        1 * gradle.addProjectEvaluationListener(_ as ImplicitTasksConfigurer);
-        1 * gradle.addProjectEvaluationListener(_ as ProjectDependencies2TaskResolver);
     }
 }
diff --git a/subprojects/core/src/test/groovy/org/gradle/configuration/DefaultScriptPluginFactoryTest.java b/subprojects/core/src/test/groovy/org/gradle/configuration/DefaultScriptPluginFactoryTest.java
index 556878e..bf55c84 100755
--- a/subprojects/core/src/test/groovy/org/gradle/configuration/DefaultScriptPluginFactoryTest.java
+++ b/subprojects/core/src/test/groovy/org/gradle/configuration/DefaultScriptPluginFactoryTest.java
@@ -15,12 +15,13 @@
  */
 package org.gradle.configuration;
 
-import org.gradle.internal.Factory;
-import org.gradle.internal.service.ServiceRegistry;
-import org.gradle.groovy.scripts.internal.ClasspathScriptTransformer;
 import org.gradle.api.internal.initialization.ScriptHandlerFactory;
 import org.gradle.api.internal.initialization.ScriptHandlerInternal;
 import org.gradle.groovy.scripts.*;
+import org.gradle.groovy.scripts.internal.ClasspathScriptTransformer;
+import org.gradle.internal.Factory;
+import org.gradle.internal.reflect.Instantiator;
+import org.gradle.internal.service.ServiceRegistry;
 import org.gradle.logging.LoggingManagerInternal;
 import org.jmock.Expectations;
 import org.jmock.Sequence;
@@ -33,7 +34,8 @@ import org.junit.runner.RunWith;
 import java.net.URL;
 import java.net.URLClassLoader;
 
-import static org.hamcrest.Matchers.*;
+import static org.hamcrest.Matchers.notNullValue;
+import static org.hamcrest.Matchers.sameInstance;
 
 @RunWith(JMock.class)
 public class DefaultScriptPluginFactoryTest {
@@ -46,6 +48,7 @@ public class DefaultScriptPluginFactoryTest {
     private final ScriptSource scriptSourceMock = context.mock(ScriptSource.class);
     private final ScriptRunner scriptRunnerMock = context.mock(ScriptRunner.class, "scriptRunner");
     private final BasicScript scriptMock = context.mock(BasicScript.class);
+    private final Instantiator instantiatorMock = context.mock(Instantiator.class);
     private final URLClassLoader parentClassLoader = new URLClassLoader(new URL[0]);
     private final URLClassLoader scriptClassLoader = new URLClassLoader(new URL[0]);
     private final ScriptHandlerFactory scriptHandlerFactoryMock = context.mock(ScriptHandlerFactory.class);
@@ -53,7 +56,7 @@ public class DefaultScriptPluginFactoryTest {
     private final ScriptRunner classPathScriptRunnerMock = context.mock(ScriptRunner.class, "classpathScriptRunner");
     private final BasicScript classPathScriptMock = context.mock(BasicScript.class, "classpathScript");
     private final Factory<LoggingManagerInternal> loggingManagerFactoryMock = context.mock(Factory.class);
-    private final DefaultScriptPluginFactory factory = new DefaultScriptPluginFactory(scriptCompilerFactoryMock, importsReaderMock, scriptHandlerFactoryMock, parentClassLoader, loggingManagerFactoryMock);
+    private final DefaultScriptPluginFactory factory = new DefaultScriptPluginFactory(scriptCompilerFactoryMock, importsReaderMock, scriptHandlerFactoryMock, parentClassLoader, loggingManagerFactoryMock, instantiatorMock);
 
     @Test
     public void configuresATargetObjectUsingScript() {
diff --git a/subprojects/core/src/test/groovy/org/gradle/configuration/ImplicitTasksConfigurerTest.groovy b/subprojects/core/src/test/groovy/org/gradle/configuration/ImplicitTasksConfigurerTest.groovy
deleted file mode 100644
index ecbeebe..0000000
--- a/subprojects/core/src/test/groovy/org/gradle/configuration/ImplicitTasksConfigurerTest.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.configuration
-
-import org.gradle.api.internal.project.ProjectInternal
-import org.gradle.util.HelperUtil
-import spock.lang.Specification
-
-class ImplicitTasksConfigurerTest extends Specification {
-    private final ImplicitTasksConfigurer configurer = new ImplicitTasksConfigurer()
-    private final ProjectInternal project = HelperUtil.createRootProject()
-
-    def "applies help tasks after evaluate"() {
-        when:
-        configurer.afterEvaluate(project, null)
-
-        then:
-        project.plugins.hasPlugin('help-tasks')
-    }
-}
diff --git a/subprojects/core/src/test/groovy/org/gradle/configuration/ImportsReaderTest.groovy b/subprojects/core/src/test/groovy/org/gradle/configuration/ImportsReaderTest.groovy
index 7ca7732..e75959a 100644
--- a/subprojects/core/src/test/groovy/org/gradle/configuration/ImportsReaderTest.groovy
+++ b/subprojects/core/src/test/groovy/org/gradle/configuration/ImportsReaderTest.groovy
@@ -19,27 +19,26 @@ package org.gradle.configuration
 import org.gradle.groovy.scripts.ScriptSource
 import org.gradle.util.Resources
 import org.junit.Rule
-import org.junit.Test
-import static org.hamcrest.Matchers.*
-import static org.junit.Assert.*
+import spock.lang.Specification
 
-/**
- * @author Hans Dockter
- */
-class ImportsReaderTest {
+class ImportsReaderTest extends Specification {
     @Rule public Resources resources = new Resources()
-    ImportsReader testObj = new ImportsReader()
+    ImportsReader reader = new ImportsReader()
 
-    @Test public void testReadImportsFromResource() {
-        String result = testObj.getImports()
-        assertEquals(resources.getResource('default-imports.txt').text, result)
+    public void testReadImportsFromResource() {
+        expect:
+        reader.imports.contains('import org.gradle.api.*')
     }
 
-    @Test public void testCreatesScriptSource() {
-        ScriptSource source = [:] as ScriptSource
-        ScriptSource importsSource = testObj.withImports(source)
-        assertThat(importsSource, instanceOf(ImportsScriptSource.class))
-        assertThat(importsSource.source, sameInstance(source))
-        assertThat(importsSource.importsReader, sameInstance(testObj))
+    public void testCreatesScriptSource() {
+        def source = [:] as ScriptSource
+
+        when:
+        def importsSource = reader.withImports(source)
+
+        then:
+        importsSource instanceof ImportsScriptSource
+        importsSource.source == source
+        importsSource.importsReader == reader
     }
 }
diff --git a/subprojects/core/src/test/groovy/org/gradle/configuration/LifecycleProjectEvaluatorTest.groovy b/subprojects/core/src/test/groovy/org/gradle/configuration/LifecycleProjectEvaluatorTest.groovy
deleted file mode 100644
index fbc6c84..0000000
--- a/subprojects/core/src/test/groovy/org/gradle/configuration/LifecycleProjectEvaluatorTest.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.configuration
-
-import org.gradle.api.ProjectEvaluationListener
-import org.gradle.api.internal.project.ProjectInternal
-import org.gradle.api.internal.project.ProjectStateInternal
-import spock.lang.Specification
-
-public class LifecycleProjectEvaluatorTest extends Specification {
-    private project = Mock(ProjectInternal)
-    private listener = Mock(ProjectEvaluationListener)
-    private delegate = Mock(ProjectEvaluator)
-    private state = Mock(ProjectStateInternal)
-    private evaluator = new LifecycleProjectEvaluator(delegate)
-
-    void setup() {
-        project.getProjectEvaluationBroadcaster() >> listener
-    }
-
-    void "nothing happens if project was already configured"() {
-        state.executed >> true
-
-        when:
-        evaluator.evaluate(project, state)
-
-        then:
-        0 * delegate._
-    }
-
-    void "nothing happens if project is being configured now"() {
-        state.executing >> true
-
-        when:
-        evaluator.evaluate(project, state)
-
-        then:
-        0 * delegate._
-    }
-    
-    void "evaluates the project firing all necessary listeners and updating the state"() {
-        when:
-        evaluator.evaluate(project, state)
-
-        then:
-        1 * listener.beforeEvaluate(project)
-        1 * state.setExecuting(true)
-
-        then:
-        1 * delegate.evaluate(project, state)
-
-        then:
-        1 * state.setExecuting(false)
-        1 * state.executed()
-        1 * listener.afterEvaluate(project, state)
-    }
-
-    void "notifies listeners and updates states even if there was evaluation failure"() {
-        def failure = new RuntimeException()
-
-        when:
-        evaluator.evaluate(project, state)
-
-        then:
-        def ex = thrown(RuntimeException)
-        ex == failure
-
-        and:
-        delegate.evaluate(project, state) >> { throw failure }
-
-        and:
-        1 * state.setExecuting(false)
-        1 * state.executed()
-        1 * listener.afterEvaluate(project, state)
-    }
-}
diff --git a/subprojects/core/src/test/groovy/org/gradle/configuration/ProjectDependencies2TaskResolverTest.groovy b/subprojects/core/src/test/groovy/org/gradle/configuration/ProjectDependencies2TaskResolverTest.groovy
deleted file mode 100644
index b11e6a8..0000000
--- a/subprojects/core/src/test/groovy/org/gradle/configuration/ProjectDependencies2TaskResolverTest.groovy
+++ /dev/null
@@ -1,40 +0,0 @@
-/*
- * Copyright 2007 the original author or authors.
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- *      http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package org.gradle.configuration
-
-import org.gradle.util.HelperUtil
-import spock.lang.Specification
-
-/**
- * @author Hans Dockter
- */
-class ProjectDependencies2TaskResolverTest extends Specification {
-    private root = HelperUtil.createRootProject()
-    private child = HelperUtil.createChildProject(root, "child")
-    private rootTask = root.tasks.add('compile')
-    private childTask = child.tasks.add('compile')
-
-    private resolver = new ProjectDependencies2TaskResolver()
-
-    void "resolves task dependencies"() {
-        child.dependsOn(root.path, false)
-        when:
-        resolver.afterEvaluate(child, null)
-        then:
-        childTask.taskDependencies.getDependencies(childTask) == [rootTask] as Set
-    }
-}
diff --git a/subprojects/core/src/test/groovy/org/gradle/configuration/project/BuildScriptProcessorTest.groovy b/subprojects/core/src/test/groovy/org/gradle/configuration/project/BuildScriptProcessorTest.groovy
new file mode 100644
index 0000000..f5ce95a
--- /dev/null
+++ b/subprojects/core/src/test/groovy/org/gradle/configuration/project/BuildScriptProcessorTest.groovy
@@ -0,0 +1,43 @@
+/*
+ * Copyright 2010 the original author or authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.gradle.configuration.project
+
+import org.gradle.api.internal.project.ProjectInternal
+import org.gradle.configuration.ScriptPlugin
+import org.gradle.configuration.ScriptPluginFactory
+import org.gradle.groovy.scripts.ScriptSource
+import spock.lang.Specification
+
+public class BuildScriptProcessorTest extends Specification {
+    def project = Mock(ProjectInternal)
+    def scriptSource = Mock(ScriptSource)
+    def configurerFactory = Mock(ScriptPluginFactory)
+    def scriptPlugin = Mock(ScriptPlugin)
+    def BuildScriptProcessor buildScriptProcessor = new BuildScriptProcessor(configurerFactory);
+
+    def "setup"() {
+        _ * project.buildScriptSource >> scriptSource
+    }
+
+    def configuresProjectUsingBuildScript() {
+        when:
+        buildScriptProcessor.execute(project)
+
+        then:
+        1 * configurerFactory.create(scriptSource) >> scriptPlugin
+        1 * scriptPlugin.apply(project)
+    }
+}
\ No newline at end of file
diff --git a/subprojects/core/src/test/groovy/org/gradle/configuration/project/ConfigureActionsProjectEvaluatorTest.groovy b/subprojects/core/src/test/groovy/org/gradle/configuration/project/ConfigureActionsProjectEvaluatorTest.groovy
new file mode 100644
index 0000000..ebde2fe
--- /dev/null
+++ b/subprojects/core/src/test/groovy/org/gradle/configuration/project/ConfigureActionsProjectEvaluatorTest.groovy
@@ -0,0 +1,59 @@
+/*
+ * Copyright 2012 the original author or authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.gradle.configuration.project
+
+import org.gradle.api.internal.project.ProjectInternal
+import org.gradle.api.internal.project.ProjectStateInternal
+import spock.lang.Specification
+
+class ConfigureActionsProjectEvaluatorTest extends Specification {
+    final def project = Mock(ProjectInternal)
+    final def state = Mock(ProjectStateInternal)
+    final def action1 = Mock(ProjectConfigureAction)
+    final def action2 = Mock(ProjectConfigureAction)
+    final def evaluator = new ConfigureActionsProjectEvaluator(action1, action2)
+
+    def "executes all configuration actions"() {
+        def project = Mock(ProjectInternal)
+
+        when:
+        evaluator.evaluate(project, state)
+
+        then:
+        1 * action1.execute(project)
+        1 * action2.execute(project)
+        0 * _._
+    }
+
+    def "does not continue executing actions when action fails"() {
+        def project = Mock(ProjectInternal)
+        def failure = new RuntimeException("Configure action failed")
+
+        when:
+        evaluator.evaluate(project, state)
+
+        then:
+        1 * action1.execute(project) >> {
+            throw failure
+        }
+        0 * _._
+
+        and:
+        def t = thrown(RuntimeException)
+        t == failure
+    }
+}
diff --git a/subprojects/core/src/test/groovy/org/gradle/configuration/project/DefaultProjectConfigurationActionContainerTest.groovy b/subprojects/core/src/test/groovy/org/gradle/configuration/project/DefaultProjectConfigurationActionContainerTest.groovy
new file mode 100644
index 0000000..30be9e5
--- /dev/null
+++ b/subprojects/core/src/test/groovy/org/gradle/configuration/project/DefaultProjectConfigurationActionContainerTest.groovy
@@ -0,0 +1,52 @@
+/*
+ * Copyright 2013 the original author or authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.gradle.configuration.project
+
+import org.gradle.api.Action
+import org.gradle.api.internal.project.ProjectInternal
+import spock.lang.Specification
+
+class DefaultProjectConfigurationActionContainerTest extends Specification {
+    def container = new DefaultProjectConfigurationActionContainer()
+
+    def "can add action to container"() {
+        def action = Mock(Action)
+
+        when:
+        container.add(action)
+
+        then:
+        container.actions == [action]
+    }
+
+    def "can add action as closure"() {
+        def run = false
+        def action = { run = true }
+
+        when:
+        container.add(action)
+
+        then:
+        container.actions.size() == 1
+
+        when:
+        container.actions[0].execute(Stub(ProjectInternal))
+
+        then:
+        run
+    }
+}
diff --git a/subprojects/core/src/test/groovy/org/gradle/configuration/project/DelayedConfigurationActionsTest.groovy b/subprojects/core/src/test/groovy/org/gradle/configuration/project/DelayedConfigurationActionsTest.groovy
new file mode 100644
index 0000000..4670551
--- /dev/null
+++ b/subprojects/core/src/test/groovy/org/gradle/configuration/project/DelayedConfigurationActionsTest.groovy
@@ -0,0 +1,70 @@
+/*
+ * Copyright 2013 the original author or authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.gradle.configuration.project
+
+import org.gradle.api.Action
+import org.gradle.api.internal.project.ProjectInternal
+import spock.lang.Specification
+
+class DelayedConfigurationActionsTest extends Specification {
+    final container = Mock(ProjectConfigurationActionContainer)
+    final project = Stub(ProjectInternal) {
+        getConfigurationActions() >> container
+    }
+    final action = new DelayedConfigurationActions()
+
+    def "runs actions and discards actions when finished"() {
+        def action1 = Mock(Action)
+        def action2 = Mock(Action)
+
+        given:
+        container.actions >> [action1, action2]
+
+        when:
+        action.execute(project)
+
+        then:
+        1 * action1.execute(project)
+        1 * action2.execute(project)
+
+        and:
+        1 * container.finished()
+    }
+
+    def "discards actions on failure"() {
+        def action1 = Mock(Action)
+        def action2 = Mock(Action)
+        def failure = new RuntimeException()
+
+        given:
+        container.actions >> [action1, action2]
+
+        when:
+        action.execute(project)
+
+        then:
+        def e = thrown(RuntimeException)
+        e == failure
+
+        and:
+        1 * action1.execute(project) >> { throw failure }
+        0 * action2._
+
+        and:
+        1 * container.finished()
+    }
+}
diff --git a/subprojects/core/src/test/groovy/org/gradle/configuration/project/LifecycleProjectEvaluatorTest.groovy b/subprojects/core/src/test/groovy/org/gradle/configuration/project/LifecycleProjectEvaluatorTest.groovy
new file mode 100644
index 0000000..76697ad
--- /dev/null
+++ b/subprojects/core/src/test/groovy/org/gradle/configuration/project/LifecycleProjectEvaluatorTest.groovy
@@ -0,0 +1,152 @@
+/*
+ * Copyright 2010 the original author or authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.gradle.configuration.project
+
+import org.gradle.api.ProjectConfigurationException
+import org.gradle.api.ProjectEvaluationListener
+import org.gradle.api.internal.project.ProjectInternal
+import org.gradle.api.internal.project.ProjectStateInternal
+import spock.lang.Specification
+
+public class LifecycleProjectEvaluatorTest extends Specification {
+    private project = Mock(ProjectInternal)
+    private listener = Mock(ProjectEvaluationListener)
+    private delegate = Mock(ProjectEvaluator)
+    private state = Mock(ProjectStateInternal)
+    private evaluator = new LifecycleProjectEvaluator(delegate)
+
+    void setup() {
+        project.getProjectEvaluationBroadcaster() >> listener
+        project.toString() >> "project1"
+    }
+
+    void "nothing happens if project was already configured"() {
+        state.executed >> true
+
+        when:
+        evaluator.evaluate(project, state)
+
+        then:
+        0 * delegate._
+    }
+
+    void "nothing happens if project is being configured now"() {
+        state.executing >> true
+
+        when:
+        evaluator.evaluate(project, state)
+
+        then:
+        0 * delegate._
+    }
+    
+    void "evaluates the project firing all necessary listeners and updating the state"() {
+        when:
+        evaluator.evaluate(project, state)
+
+        then:
+        1 * listener.beforeEvaluate(project)
+        1 * state.setExecuting(true)
+
+        then:
+        1 * delegate.evaluate(project, state)
+
+        then:
+        1 * state.setExecuting(false)
+        1 * state.executed()
+        1 * listener.afterEvaluate(project, state)
+    }
+
+    void "notifies listeners and updates state on evaluation failure"() {
+        def failure = new RuntimeException()
+
+        when:
+        evaluator.evaluate(project, state)
+
+        then:
+        delegate.evaluate(project, state) >> { throw failure }
+
+        and:
+        1 * state.executed({
+            assertIsConfigurationFailure(it, failure)
+        })
+        1 * state.setExecuting(false)
+        1 * listener.afterEvaluate(project, state)
+    }
+
+    void "updates state and does not delegate when beforeEvaluate action fails"() {
+        def failure = new RuntimeException()
+
+        when:
+        evaluator.evaluate(project, state)
+
+        then:
+        1 * listener.beforeEvaluate(project) >> { throw failure }
+        1 * state.executed({
+            assertIsConfigurationFailure(it, failure)
+        })
+        0 * delegate._
+        0 * listener._
+    }
+
+    void "updates state when afterEvaluate action fails"() {
+        def failure = new RuntimeException()
+
+        when:
+        evaluator.evaluate(project, state)
+
+        then:
+        delegate.evaluate(project, state)
+
+        and:
+        1 * state.setExecuting(false)
+        1 * state.executed()
+
+        then:
+        1 * listener.afterEvaluate(project, state) >> { throw failure }
+        1 * state.executed({
+            assertIsConfigurationFailure(it, failure)
+        })
+    }
+
+    def assertIsConfigurationFailure(def it, def cause) {
+        assert it instanceof ProjectConfigurationException
+        assert it.message == "A problem occurred configuring project1."
+        assert it.cause == cause
+        true
+    }
+
+    void "notifies listeners and updates state on evaluation failure even if afterEvaluate fails"() {
+        def failure = new RuntimeException()
+
+        when:
+        evaluator.evaluate(project, state)
+
+        then:
+        delegate.evaluate(project, state) >> { throw failure }
+
+        and:
+        _ * project.toString() >> "project1"
+        1 * state.executed(_)
+        1 * state.setExecuting(false)
+
+        then:
+        1 * listener.afterEvaluate(project, state) >> { throw new RuntimeException("afterEvaluate")}
+        1 * state.hasFailure() >> true
+        0 * state.executed(_)
+    }
+}
diff --git a/subprojects/core/src/test/groovy/org/gradle/configuration/project/PluginsProjectConfigureActionsTest.groovy b/subprojects/core/src/test/groovy/org/gradle/configuration/project/PluginsProjectConfigureActionsTest.groovy
new file mode 100644
index 0000000..d4ec1aa
--- /dev/null
+++ b/subprojects/core/src/test/groovy/org/gradle/configuration/project/PluginsProjectConfigureActionsTest.groovy
@@ -0,0 +1,52 @@
+/*
+ * Copyright 2012 the original author or authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.gradle.configuration.project
+import org.gradle.api.internal.project.ProjectInternal
+import spock.lang.Specification
+
+class PluginsProjectConfigureActionsTest extends Specification {
+    final def pluginsClassLoader = Mock(ClassLoader)
+    final def evaluator = new PluginsProjectConfigureActions(pluginsClassLoader)
+
+    def "executes all implicit configuration actions"() {
+        def project = Mock(ProjectInternal)
+
+        when:
+        evaluator.execute(project)
+
+        then:
+        1 * pluginsClassLoader.getResources('META-INF/services/org.gradle.configuration.project.ProjectConfigureAction') >> resources()
+        1 * pluginsClassLoader.loadClass('ConfigureActionClass') >> TestConfigureAction
+        1 * project.setVersion(12)
+        0 * _._
+    }
+
+    def resources() {
+        URLStreamHandler handler = Mock()
+        URLConnection connection = Mock()
+        URL url = new URL("custom", "host", 12, "file", handler)
+        _ * handler.openConnection(url) >> connection
+        _ * connection.getInputStream() >> new ByteArrayInputStream("ConfigureActionClass".bytes)
+        return Collections.enumeration([url])
+    }
+
+    static class TestConfigureAction implements ProjectConfigureAction {
+        void execute(ProjectInternal project) {
+            project.version = 12
+        }
+    }
+}
diff --git a/subprojects/core/src/test/groovy/org/gradle/configuration/project/ProjectDependencies2TaskResolverTest.groovy b/subprojects/core/src/test/groovy/org/gradle/configuration/project/ProjectDependencies2TaskResolverTest.groovy
new file mode 100644
index 0000000..9be41ed
--- /dev/null
+++ b/subprojects/core/src/test/groovy/org/gradle/configuration/project/ProjectDependencies2TaskResolverTest.groovy
@@ -0,0 +1,37 @@
+/*
+ * Copyright 2007 the original author or authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.gradle.configuration.project
+
+import org.gradle.util.TestUtil
+import spock.lang.Specification
+
+class ProjectDependencies2TaskResolverTest extends Specification {
+    private root = TestUtil.createRootProject()
+    private child = TestUtil.createChildProject(root, "child")
+    private rootTask = root.tasks.create('compile')
+    private childTask = child.tasks.create('compile')
+
+    private resolver = new ProjectDependencies2TaskResolver()
+
+    void "resolves task dependencies"() {
+        child.dependsOn(root.path, false)
+        when:
+        resolver.execute(child)
+        then:
+        childTask.taskDependencies.getDependencies(childTask) == [rootTask] as Set
+    }
+}
diff --git a/subprojects/core/src/test/groovy/org/gradle/execution/ProjectEvaluatingActionTest.groovy b/subprojects/core/src/test/groovy/org/gradle/execution/ProjectEvaluatingActionTest.groovy
index 7ffe1f2..abd3159 100644
--- a/subprojects/core/src/test/groovy/org/gradle/execution/ProjectEvaluatingActionTest.groovy
+++ b/subprojects/core/src/test/groovy/org/gradle/execution/ProjectEvaluatingActionTest.groovy
@@ -21,9 +21,6 @@ import org.gradle.api.internal.GradleInternal
 import org.gradle.api.internal.project.DefaultProject
 import spock.lang.Specification
 
-/**
- * by Szczepan Faber, created at: 1/8/13
- */
 class ProjectEvaluatingActionTest extends Specification {
 
     private evaluator = Mock(TaskPathProjectEvaluator)
diff --git a/subprojects/core/src/test/groovy/org/gradle/execution/TaskNameResolverTest.groovy b/subprojects/core/src/test/groovy/org/gradle/execution/TaskNameResolverTest.groovy
index 1f56ba4..f9dda3a 100644
--- a/subprojects/core/src/test/groovy/org/gradle/execution/TaskNameResolverTest.groovy
+++ b/subprojects/core/src/test/groovy/org/gradle/execution/TaskNameResolverTest.groovy
@@ -27,15 +27,15 @@ class TaskNameResolverTest extends Specification {
         ProjectInternal project = Mock()
         TaskContainerInternal tasks = Mock()
         _ * project.tasks >> tasks
-
         Task task = task('task')
+        1 * tasks.getByName('task') >> task
 
         when:
         def candidates = resolver.select('task', project)
 
         then:
         1 * tasks.findByName('task') >> task
-        candidates.get('task') == [task] as Set
+        asTasks(candidates.get("task")) == [task] as Set
     }
 
     def selectsImplicitTaskForSingleProjectWhenThereIsAnExactMatchOnName() {
@@ -46,6 +46,7 @@ class TaskNameResolverTest extends Specification {
         _ * project.implicitTasks >> implicitTasks
 
         Task task = task('task')
+        1 * implicitTasks.getByName('task') >> task
 
         when:
         def candidates = resolver.select('task', project)
@@ -53,7 +54,7 @@ class TaskNameResolverTest extends Specification {
         then:
         1 * tasks.findByName('task') >> null
         1 * implicitTasks.findByName('task') >> task
-        candidates.get('task') == [task] as Set
+        asTasks(candidates.get('task')) == [task] as Set
     }
 
     def selectsAllTasksForSingleProjectWhenThereIsNoExactMatchOnName() {
@@ -65,7 +66,8 @@ class TaskNameResolverTest extends Specification {
 
         Task task1 = task('task1')
         Task task2 = task('task2')
-        Task hidden = task('task1')
+        1 * tasks.getByName('task1') >> task1
+        1 * implicitTasks.getByName('task2') >> task2
 
         when:
         def candidates = resolver.select('task', project)
@@ -73,12 +75,13 @@ class TaskNameResolverTest extends Specification {
         then:
         1 * tasks.findByName('task') >> null
         1 * implicitTasks.findByName('task') >> null
-        1 * tasks.iterator() >> [task1].iterator()
-        1 * implicitTasks.iterator() >> [task2, hidden].iterator()
-        candidates.get('task1') == [task1] as Set
-        candidates.get('task2') == [task2] as Set
+        1 * tasks.names >> (['task1'] as SortedSet)
+        1 * implicitTasks.names >> (['task2', 'task1'] as SortedSet)
+        asTasks(candidates.get('task1')) == [task1] as Set
+        asTasks(candidates.get('task2')) == [task2] as Set
     }
 
+
     def selectsTasksForMultipleProjectsWhenThereIsAnExactMatchOnName() {
         ProjectInternal project = Mock()
         TaskContainerInternal tasks = Mock()
@@ -89,7 +92,9 @@ class TaskNameResolverTest extends Specification {
         _ * childProject.tasks >> childProjectTasks
 
         Task task1 = task('task')
+        _ * tasks.getByName('task') >> task1
         Task task2 = task('task')
+        _ * childProjectTasks.getByName('task') >> task2
 
         when:
         def candidates = resolver.selectAll('task', project)
@@ -97,7 +102,7 @@ class TaskNameResolverTest extends Specification {
         then:
         1 * tasks.findByName('task') >> task1
         1 * childProjectTasks.findByName('task') >> task2
-        candidates.get('task') == [task1, task2] as Set
+        asTasks(candidates.get('task')) == [task1, task2] as Set
     }
 
     def selectsImplicitTaskForMultipleProjectsWhenThereIsAnExactMatchOnName() {
@@ -112,7 +117,9 @@ class TaskNameResolverTest extends Specification {
         _ * childProject.tasks >> childProjectTasks
 
         Task task1 = task('task')
+        _ * implicitTasks.getByName('task') >> task1
         Task task2 = task('task')
+        _ * childProjectTasks.getByName('task') >> task2
 
         when:
         def candidates = resolver.selectAll('task', project)
@@ -121,7 +128,7 @@ class TaskNameResolverTest extends Specification {
         1 * tasks.findByName('task') >> null
         1 * implicitTasks.findByName('task') >> task1
         1 * childProjectTasks.findByName('task') >> task2
-        candidates.get('task') == [task1, task2] as Set
+        asTasks(candidates.get('task')) == [task1, task2] as Set
     }
 
     def selectsAllTasksForMultipleProjectsWhenThereIsNoExactMatchOnName() {
@@ -139,6 +146,13 @@ class TaskNameResolverTest extends Specification {
         Task task2 = task('name2')
         Task task3 = task('name1')
         Task task4 = task('name2')
+        Task task5 = task('name3')
+
+        _ * childProjectTasks.getByName(task3.name) >> task3
+        _ * childProjectTasks.getByName(task4.name) >> task4
+        _ * childProjectTasks.getByName(task5.name) >> task5
+        _ * tasks.getByName(task1.name) >> task1
+        _ * implicitTasks.getByName(task2.name) >> task2
 
         when:
         def candidates = resolver.selectAll('task', project)
@@ -147,11 +161,13 @@ class TaskNameResolverTest extends Specification {
         1 * tasks.findByName('task') >> null
         1 * implicitTasks.findByName('task') >> null
         1 * childProjectTasks.findByName('task') >> null
-        1 * tasks.iterator() >> [task1].iterator()
-        1 * implicitTasks.iterator() >> [task2].iterator()
-        1 * childProjectTasks.iterator() >> [task3, task4].iterator()
-        candidates.get('name1') == [task1, task3] as Set
-        candidates.get('name2') == [task2, task4] as Set
+        1 * tasks.names >> ([task1.name] as SortedSet)
+        1 * implicitTasks.names >> ([task2.name] as SortedSet)
+        1 * childProjectTasks.names >> ([task3.name, task4.name, task5.name] as SortedSet)
+
+        asTasks(candidates.get('name1')) == [task1, task3] as Set
+        asTasks(candidates.get('name2')) == [task2, task4] as Set
+        asTasks(candidates.get('name3')) == [task5] as Set
     }
 
     def task(String name) {
@@ -159,4 +175,8 @@ class TaskNameResolverTest extends Specification {
         _ * task.name >> name
         return task
     }
+
+    Set<Task> asTasks(Set<TaskSelectionResult> taskSelectionResults) {
+        taskSelectionResults.collect { it.getTask() }.toSet()
+    }
 }
diff --git a/subprojects/core/src/test/groovy/org/gradle/execution/TaskNameResolvingBuildConfigurationActionTest.java b/subprojects/core/src/test/groovy/org/gradle/execution/TaskNameResolvingBuildConfigurationActionTest.java
index 7314ea3..de4e2b0 100644
--- a/subprojects/core/src/test/groovy/org/gradle/execution/TaskNameResolvingBuildConfigurationActionTest.java
+++ b/subprojects/core/src/test/groovy/org/gradle/execution/TaskNameResolvingBuildConfigurationActionTest.java
@@ -42,7 +42,7 @@ import static org.hamcrest.Matchers.equalTo;
 import static org.junit.Assert.assertThat;
 import static org.junit.Assert.fail;
 
- at RunWith (org.jmock.integration.junit4.JMock.class)
+ at RunWith(org.jmock.integration.junit4.JMock.class)
 public class TaskNameResolvingBuildConfigurationActionTest {
     private final JUnit4Mockery context = new JUnit4GroovyMockery();
     private final ProjectInternal project = context.mock(AbstractProject.class, "[project]");
@@ -57,7 +57,7 @@ public class TaskNameResolvingBuildConfigurationActionTest {
 
     @Before
     public void setUp() {
-        context.checking(new Expectations(){{
+        context.checking(new Expectations() {{
             allowing(executionContext).getGradle();
             will(returnValue(gradle));
             allowing(gradle).getDefaultProject();
@@ -132,13 +132,13 @@ public class TaskNameResolvingBuildConfigurationActionTest {
 
         action.configure(executionContext);
     }
-    
+
     @Test
     public void selectsTaskWithMatchingRelativePath() {
         final Task task1 = task("b");
         final Task task2 = task("a");
 
-        context.checking(new Expectations(){{
+        context.checking(new Expectations() {{
             allowing(startParameter).getTaskNames();
             will(returnValue(toList("a:b")));
 
@@ -158,7 +158,7 @@ public class TaskNameResolvingBuildConfigurationActionTest {
         final Task task1 = task("b");
         final Task task2 = task("a");
 
-        context.checking(new Expectations(){{
+        context.checking(new Expectations() {{
             allowing(startParameter).getTaskNames();
             will(returnValue(toList(":b")));
 
@@ -178,7 +178,7 @@ public class TaskNameResolvingBuildConfigurationActionTest {
         final Task task1 = task("b");
         final Task task2 = task("a");
 
-        context.checking(new Expectations(){{
+        context.checking(new Expectations() {{
             allowing(startParameter).getTaskNames();
             will(returnValue(toList(":a:b")));
 
@@ -200,7 +200,7 @@ public class TaskNameResolvingBuildConfigurationActionTest {
         final Task task1 = task("someTask");
         final Task task2 = task("other");
 
-        context.checking(new Expectations(){{
+        context.checking(new Expectations() {{
             allowing(startParameter).getTaskNames();
             will(returnValue(toList("anotherProject:soTa")));
 
@@ -220,7 +220,7 @@ public class TaskNameResolvingBuildConfigurationActionTest {
         final Task task1 = task("someTask");
         final Task task2 = task("other");
 
-        context.checking(new Expectations(){{
+        context.checking(new Expectations() {{
             allowing(startParameter).getTaskNames();
             will(returnValue(toList("anPr:soTa")));
 
@@ -384,27 +384,40 @@ public class TaskNameResolvingBuildConfigurationActionTest {
 
     private <T extends Task> T task(final String name, Class<T> taskType) {
         final T task = context.mock(taskType);
-        context.checking(new Expectations(){{
+        context.checking(new Expectations() {{
             allowing(task).getName();
             will(returnValue(name));
         }});
         return task;
     }
 
-    private Multimap<String, Task> tasks(Task... tasks) {
+    private Multimap<String, TaskSelectionResult> tasks(Task... tasks) {
         return tasks(Arrays.asList(tasks));
     }
 
-    private Multimap<String, Task> tasks(Iterable<Task> tasks) {
-        Multimap<String, Task> map = LinkedHashMultimap.create();
-        for (Task task : tasks) {
-            map.put(task.getName(), task);
+    private Multimap<String, TaskSelectionResult> tasks(Iterable<Task> tasks) {
+        Multimap<String, TaskSelectionResult> map = LinkedHashMultimap.create();
+        for (final Task task : tasks) {
+            map.put(task.getName(), new SimpleTaskSelectionResult(task));
         }
         return map;
     }
 
+    private static class SimpleTaskSelectionResult implements TaskSelectionResult {
+        private final Task task;
+
+        public SimpleTaskSelectionResult(Task task) {
+            this.task = task;
+        }
+
+        public Task getTask() {
+            return task;
+        }
+    }
+
     public abstract class TaskWithBooleanProperty implements Task {
         @CommandLineOption(options = "all", description = "Some boolean flag")
-        public void setSomeFlag(boolean flag) { }
+        public void setSomeFlag(boolean flag) {
+        }
     }
 }
diff --git a/subprojects/core/src/test/groovy/org/gradle/execution/TaskPathProjectEvaluatorTest.groovy b/subprojects/core/src/test/groovy/org/gradle/execution/TaskPathProjectEvaluatorTest.groovy
index 4a8306c..214d9bb 100644
--- a/subprojects/core/src/test/groovy/org/gradle/execution/TaskPathProjectEvaluatorTest.groovy
+++ b/subprojects/core/src/test/groovy/org/gradle/execution/TaskPathProjectEvaluatorTest.groovy
@@ -21,9 +21,6 @@ import org.gradle.execution.taskpath.ResolvedTaskPath
 import org.gradle.execution.taskpath.TaskPathResolver
 import spock.lang.Specification
 
-/**
- * by Szczepan Faber, created at: 1/8/13
- */
 class TaskPathProjectEvaluatorTest extends Specification {
 
     private resolver = Mock(TaskPathResolver)
diff --git a/subprojects/core/src/test/groovy/org/gradle/execution/commandline/CommandLineTaskConfigurerSpec.groovy b/subprojects/core/src/test/groovy/org/gradle/execution/commandline/CommandLineTaskConfigurerSpec.groovy
index 8742df0..db442aa 100644
--- a/subprojects/core/src/test/groovy/org/gradle/execution/commandline/CommandLineTaskConfigurerSpec.groovy
+++ b/subprojects/core/src/test/groovy/org/gradle/execution/commandline/CommandLineTaskConfigurerSpec.groovy
@@ -27,9 +27,6 @@ import org.gradle.execution.TaskSelector
 import org.gradle.testfixtures.ProjectBuilder
 import spock.lang.Specification
 
-/**
- * by Szczepan Faber, created at: 10/8/12
- */
 class CommandLineTaskConfigurerSpec extends Specification {
 
     Project project = new ProjectBuilder().build()
diff --git a/subprojects/core/src/test/groovy/org/gradle/execution/commandline/CommandLineTaskParserSpec.groovy b/subprojects/core/src/test/groovy/org/gradle/execution/commandline/CommandLineTaskParserSpec.groovy
index 269f3ad..6523fe7 100644
--- a/subprojects/core/src/test/groovy/org/gradle/execution/commandline/CommandLineTaskParserSpec.groovy
+++ b/subprojects/core/src/test/groovy/org/gradle/execution/commandline/CommandLineTaskParserSpec.groovy
@@ -19,6 +19,7 @@ package org.gradle.execution.commandline
 import org.gradle.api.DefaultTask
 import org.gradle.api.Project
 import org.gradle.api.tasks.TaskAction
+import org.gradle.execution.TaskSelectionResult
 import org.gradle.execution.TaskSelector
 import org.gradle.testfixtures.ProjectBuilder
 import spock.lang.Specification
@@ -26,9 +27,6 @@ import spock.lang.Specification
 import static com.google.common.collect.Sets.newHashSet
 import static java.util.Collections.emptyList
 
-/**
- * by Szczepan Faber, created at: 10/8/12
- */
 class CommandLineTaskParserSpec extends Specification {
 
     Project project = new ProjectBuilder().build()
@@ -50,7 +48,7 @@ class CommandLineTaskParserSpec extends Specification {
 
     def "parses a single task"() {
         given:
-        selector.getSelection('foo') >> new TaskSelector.TaskSelection('foo task', [task] as Set)
+        selector.getSelection('foo') >> new TaskSelector.TaskSelection('foo task', asTaskSelectionResults(task))
 
         when:
         def out = parser.parseTasks(['foo'], selector)
@@ -60,9 +58,17 @@ class CommandLineTaskParserSpec extends Specification {
         out.get('foo task') == [task] as Set
     }
 
+    Set<TaskSelectionResult> asTaskSelectionResults(SomeTask... someTasks) {
+        return someTasks.collect {task ->
+            TaskSelectionResult mock = Mock(TaskSelectionResult)
+            _ * mock.task >> task
+            mock
+        }
+    }
+
     def "parses single task with multiple matches"() {
         given:
-        selector.getSelection('foo') >> new TaskSelector.TaskSelection('foo task', [task, task2] as Set)
+        selector.getSelection('foo') >> new TaskSelector.TaskSelection('foo task', asTaskSelectionResults(task, task2))
 
         when:
         def out = parser.parseTasks(['foo'], selector)
@@ -74,8 +80,8 @@ class CommandLineTaskParserSpec extends Specification {
 
     def "parses multiple matching tasks"() {
         given:
-        selector.getSelection('foo') >> new TaskSelector.TaskSelection('foo task', [task, task2] as Set)
-        selector.getSelection('bar') >> new TaskSelector.TaskSelection('bar task', [task3] as Set)
+        selector.getSelection('foo') >> new TaskSelector.TaskSelection('foo task', asTaskSelectionResults(task, task2))
+        selector.getSelection('bar') >> new TaskSelector.TaskSelection('bar task', asTaskSelectionResults(task3))
 
         when:
         def out = parser.parseTasks(['foo', 'bar'], selector)
@@ -88,9 +94,9 @@ class CommandLineTaskParserSpec extends Specification {
 
     def "configures tasks if configuration options specified"() {
         given:
-        selector.getSelection('foo') >> new TaskSelector.TaskSelection('foo task', [task, task2] as Set)
-        selector.getSelection('bar') >> new TaskSelector.TaskSelection('bar task', [task3] as Set)
-        selector.getSelection('lastTask') >> new TaskSelector.TaskSelection('last task', [task3] as Set)
+        selector.getSelection('foo') >> new TaskSelector.TaskSelection('foo task', asTaskSelectionResults(task, task2))
+        selector.getSelection('bar') >> new TaskSelector.TaskSelection('bar task', asTaskSelectionResults(task3))
+        selector.getSelection('lastTask') >> new TaskSelector.TaskSelection('last task', asTaskSelectionResults(task3))
 
         when:
         def out = parser.parseTasks(['foo', '--all', 'bar', '--include', 'stuff', 'lastTask'], selector)
diff --git a/subprojects/core/src/test/groovy/org/gradle/execution/taskgraph/DefaultTaskExecutionPlanTest.groovy b/subprojects/core/src/test/groovy/org/gradle/execution/taskgraph/DefaultTaskExecutionPlanTest.groovy
index fc8f67e..86215cf 100644
--- a/subprojects/core/src/test/groovy/org/gradle/execution/taskgraph/DefaultTaskExecutionPlanTest.groovy
+++ b/subprojects/core/src/test/groovy/org/gradle/execution/taskgraph/DefaultTaskExecutionPlanTest.groovy
@@ -14,83 +14,86 @@
  * limitations under the License.
  */
 
-package org.gradle.execution.taskgraph;
-
+package org.gradle.execution.taskgraph
 
 import org.gradle.api.CircularReferenceException
 import org.gradle.api.Task
 import org.gradle.api.internal.TaskInternal
-import org.gradle.api.internal.changedetection.TaskArtifactStateCacheAccess
-import org.gradle.api.internal.project.ProjectInternal
+import org.gradle.api.internal.project.DefaultProject
 import org.gradle.api.specs.Spec
-import org.gradle.api.specs.Specs
 import org.gradle.api.tasks.TaskDependency
 import org.gradle.api.tasks.TaskState
-import org.gradle.cache.PersistentIndexedCache
 import org.gradle.execution.TaskFailureHandler
-import org.gradle.internal.Factory
-import org.gradle.messaging.serialize.Serializer
-import org.hamcrest.Description
-import org.jmock.api.Invocation
+import org.gradle.util.TextUtil
 import spock.lang.Specification
 
-import static org.gradle.util.HelperUtil.createRootProject
+import static org.gradle.util.TestUtil.createChildProject
+import static org.gradle.util.TestUtil.createRootProject
 import static org.gradle.util.WrapUtil.toList
-import static org.gradle.util.WrapUtil.toSet
 
 public class DefaultTaskExecutionPlanTest extends Specification {
 
     DefaultTaskExecutionPlan executionPlan
-    ProjectInternal root;
-    Spec<TaskInfo> anyTask = Specs.satisfyAll();
+    DefaultProject root;
 
     def setup() {
         root = createRootProject();
         executionPlan = new DefaultTaskExecutionPlan()
     }
 
-    def "returns tasks in dependency order"() {
+    private void addToGraphAndPopulate(List tasks) {
+        executionPlan.addToTaskGraph(tasks)
+        executionPlan.determineExecutionPlan()
+    }
+
+    private TaskFailureHandler createIgnoreTaskFailureHandler(Task task) {
+        Mock(TaskFailureHandler) {
+            onTaskFailure(task) >> {}
+        }
+    }
+
+    def "schedules tasks in dependency order"() {
         given:
         Task a = task("a");
-        Task b = task("b", a);
-        Task c = task("c", b, a);
-        Task d = task("d", c);
+        Task b = task("b", dependsOn: [a]);
+        Task c = task("c", dependsOn: [b, a]);
+        Task d = task("d", dependsOn: [c]);
 
         when:
-        executionPlan.addToTaskGraph(toList(d))
+        addToGraphAndPopulate([d])
 
         then:
-        executedTasks == [a, b, c, d]
+        executes(a, b, c, d)
     }
 
-    def "returns task dependencies in name order"() {
+    def "schedules task dependencies in name order when there are no dependencies between them"() {
         given:
         Task a = task("a");
         Task b = task("b");
         Task c = task("c");
-        Task d = task("d", b, a, c);
+        Task d = task("d", dependsOn: [b, a, c]);
 
         when:
-        executionPlan.addToTaskGraph(toList(d));
+        addToGraphAndPopulate([d])
 
         then:
-        executedTasks == [a, b, c, d]
+        executes(a, b, c, d)
     }
 
-    def "returns a single batch of tasks in name order"() {
+    def "schedules a single batch of tasks in name order"() {
         given:
         Task a = task("a");
         Task b = task("b");
         Task c = task("c");
 
         when:
-        executionPlan.addToTaskGraph(toList(b, c, a));
+        addToGraphAndPopulate(toList(b, c, a));
 
         then:
-        executedTasks == [a, b, c]
+        executes(a, b, c)
     }
 
-    def "returns separately added tasks in order added"() {
+    def "schedules separately added tasks in order added"() {
         given:
         Task a = task("a");
         Task b = task("b");
@@ -99,73 +102,287 @@ public class DefaultTaskExecutionPlanTest extends Specification {
 
         when:
         executionPlan.addToTaskGraph(toList(c, b));
-        executionPlan.addToTaskGraph(toList(d, a));
+        executionPlan.addToTaskGraph(toList(d, a))
+        executionPlan.determineExecutionPlan()
+
+        then:
+        executes(b, c, a, d)
+    }
+
+    def "schedules must run after task dependencies in name order"() {
+        given:
+        Task a = task("a");
+        Task b = task("b");
+        Task c = task("c", mustRunAfter: [b, a]);
+        Task d = task("d", dependsOn: [b, a]);
+
+        when:
+        addToGraphAndPopulate([c, d]);
 
         then:
-        executedTasks == [b, c, a, d];
+        executes(a, b, c, d)
     }
 
-    def "common tasks in separate batches are returned only once"() {
+    def "common tasks in separate batches are schedules only once"() {
         Task a = task("a");
         Task b = task("b");
-        Task c = task("c", a, b);
+        Task c = task("c", dependsOn: [a, b]);
         Task d = task("d");
-        Task e = task("e", b, d);
+        Task e = task("e", dependsOn: [b, d]);
 
         when:
         executionPlan.addToTaskGraph(toList(c));
         executionPlan.addToTaskGraph(toList(e));
+        executionPlan.determineExecutionPlan();
 
         then:
-        executedTasks == [a, b, c, d, e];
+        executes(a, b, c, d, e)
     }
 
-    def "all dependencies added when adding tasks"() {
+    def "all dependencies scheduled when adding tasks"() {
         Task a = task("a");
-        Task b = task("b", a);
-        Task c = task("c", b, a);
-        Task d = task("d", c);
+        Task b = task("b", dependsOn: [a]);
+        Task c = task("c", dependsOn: [b, a]);
+        Task d = task("d", dependsOn: [c]);
 
         when:
-        executionPlan.addToTaskGraph(toList(d));
+        addToGraphAndPopulate(toList(d));
 
         then:
-        executionPlan.getTasks() == [a, b, c, d];
-        executedTasks == [a, b, c, d]
+        executes(a, b, c, d)
     }
 
-    def "getAllTasks returns tasks in execution order"() {
-        Task d = task("d");
-        Task c = task("c");
-        Task b = task("b", d, c);
-        Task a = task("a", b);
+    def "must run after ordering is honoured for tasks added separately to graph"() {
+        Task a = task("a")
+        Task b = task("b", dependsOn: [a])
+        Task c = task("c", mustRunAfter: [b])
+
+        when:
+        executionPlan.addToTaskGraph([c])
+        executionPlan.addToTaskGraph([b])
+        executionPlan.determineExecutionPlan()
+
+        then:
+        executes(a, b, c)
+    }
+
+    def "must run after ordering is honoured for dependencies"() {
+        Task b = task("b")
+        Task a = task("a", mustRunAfter: [b])
+        Task c = task("c", dependsOn: [a, b])
+
+        when:
+        addToGraphAndPopulate([c])
+
+        then:
+        executes(b, a, c)
+    }
+
+    def "mustRunAfter dependencies are scheduled before regular dependencies"() {
+        Task a = task("a")
+        Task b = task("b")
+        Task c = task("c", dependsOn: [a], mustRunAfter: [b])
+        Task d = task("d", dependsOn: [b])
+
+        when:
+        addToGraphAndPopulate([c, d])
+
+        then:
+        executes(b, a, c, d)
+    }
+
+    def "must run after does not pull in tasks that are not in the graph"() {
+        Task a = task("a")
+        Task b = task("b", mustRunAfter: [a])
+
+        when:
+        addToGraphAndPopulate([b])
+
+        then:
+        executes(b)
+    }
+
+    def "finalizer tasks are executed if a finalized task is added to the graph"() {
+        Task finalizer = task("a")
+        Task finalized = task("b", finalizedBy: [finalizer])
+
+        when:
+        addToGraphAndPopulate([finalized])
+
+        then:
+        executes(finalized, finalizer)
+    }
+
+    def "finalizer tasks and their dependencies are executed even in case of a task failure"() {
+        Task finalizerDependency = task("finalizerDependency")
+        Task finalizer1 = task("finalizer1", dependsOn: [finalizerDependency])
+        Task finalized1 = task("finalized1", finalizedBy: [finalizer1])
+        Task finalizer2 = task("finalizer2")
+        Task finalized2 = task("finalized2", finalizedBy: [finalizer2], failure: new RuntimeException("failure"))
+
+        when:
+        addToGraphAndPopulate([finalized1, finalized2])
+
+        then:
+        executes(finalized1, finalizerDependency, finalizer1, finalized2, finalizer2)
+    }
+
+    def "finalizer task is not added to the graph if it is filtered"() {
+        given:
+        Task finalizer = filteredTask("finalizer")
+        Task finalized = task("finalized", finalizedBy: [finalizer])
+        Spec<Task> filter = Mock() {
+            isSatisfiedBy(_) >> { Task t -> t != finalizer }
+        }
+
+        when:
+        executionPlan.useFilter(filter);
+        addToGraphAndPopulate([finalized])
+
+        then:
+        executes(finalized)
+    }
+
+    def "finalizer tasks and their dependencies are not executed if finalized task did not run"() {
+        Task finalizerDependency = task("finalizerDependency")
+        Task finalizer = task("finalizer", dependsOn: [finalizerDependency])
+        Task finalizedDependency = task("finalizedDependency", failure: new RuntimeException("failure"))
+        Task finalized = task("finalized", dependsOn: [finalizedDependency], finalizedBy: [finalizer])
+
+        when:
+        addToGraphAndPopulate([finalized])
+
+        then:
+        executionPlan.tasks == [finalizedDependency, finalized, finalizerDependency, finalizer]
+        executedTasks == [finalizedDependency]
+    }
+
+    def "finalizer tasks and their dependencies are executed if they are previously required even if the finalized task did not run"() {
+        Task finalizerDependency = task("finalizerDependency")
+        Task finalizer = task("finalizer", dependsOn: [finalizerDependency])
+        Task finalizedDependency = task("finalizedDependency", failure: new RuntimeException("failure"))
+        Task finalized = task("finalized", dependsOn: [finalizedDependency], finalizedBy: [finalizer])
+        executionPlan.useFailureHandler(createIgnoreTaskFailureHandler(finalizedDependency));
+
+        when:
+        addToGraphAndPopulate([finalizer, finalized])
+
+        then:
+        executionPlan.tasks == [finalizedDependency, finalized, finalizerDependency, finalizer]
+        executedTasks == [finalizedDependency, finalizerDependency, finalizer]
+    }
+
+    def "finalizer tasks and their dependencies are executed if they are later required via dependency even if the finalized task did not do any work"() {
+        Task finalizerDependency = task("finalizerDependency")
+        Task finalizer = task("finalizer", dependsOn: [finalizerDependency])
+        Task dependsOnFinalizer = task("dependsOnFinalizer", dependsOn: [finalizer])
+        Task finalized = task("finalized", finalizedBy: [finalizer], didWork: false)
 
         when:
-        executionPlan.addToTaskGraph(toList(a));
+        executionPlan.addToTaskGraph([finalized])
+        executionPlan.addToTaskGraph([dependsOnFinalizer])
+        executionPlan.determineExecutionPlan()
 
         then:
-        executionPlan.getTasks() == [c, d, b, a]
-        executedTasks == [c, d, b, a]
+        executes(finalized, finalizerDependency, finalizer, dependsOnFinalizer)
+    }
+
+    def "finalizer tasks run as soon as possible for tasks that depend on finalized tasks"() {
+        Task finalizer = task("finalizer")
+        Task finalized = task("finalized", finalizedBy: [finalizer])
+        Task dependsOnFinalized = task("dependsOnFinalized", dependsOn: [finalized])
+
+        when:
+        addToGraphAndPopulate([dependsOnFinalized])
+
+        then:
+        executes(finalized, finalizer, dependsOnFinalized)
+    }
+
+    def "finalizer tasks run as soon as possible for tasks that must run after finalized tasks"() {
+        Task finalizer = task("finalizer")
+        Task finalized = task("finalized", finalizedBy: [finalizer])
+        Task mustRunAfterFinalized = task("mustRunAfterFinalized", mustRunAfter: [finalized])
+
+        when:
+        addToGraphAndPopulate([mustRunAfterFinalized, finalized])
+
+        then:
+        executes(finalized, finalizer, mustRunAfterFinalized)
     }
 
     def "cannot add task with circular reference"() {
-        Task a = createTask("a");
-        Task b = task("b", a);
-        Task c = task("c", b);
-        dependsOn(a, c);
+        Task a = createTask("a")
+        Task b = task("b", dependsOn: [a])
+        Task c = task("c", dependsOn: [b])
+        Task d = task("d")
+        relationships(a, dependsOn: [c, d])
 
         when:
-        executionPlan.addToTaskGraph([c])
+        addToGraphAndPopulate([c])
 
         then:
-        thrown CircularReferenceException
+        def e = thrown CircularReferenceException
+        e.message == TextUtil.toPlatformLineSeparators("""Circular dependency between the following tasks:
+:a
+\\--- :c
+     \\--- :b
+          \\--- :a (*)
+
+(*) - details omitted (listed previously)
+""")
+    }
+
+    def "cannot add a task with must run after induced circular reference"() {
+        Task a = createTask("a")
+        Task b = task("b", mustRunAfter: [a])
+        Task c = task("c", dependsOn: [b])
+        relationships(a, dependsOn: [c])
+
+        when:
+        addToGraphAndPopulate([a])
+
+        then:
+        def e = thrown CircularReferenceException
+        e.message == TextUtil.toPlatformLineSeparators("""Circular dependency between the following tasks:
+:a
+\\--- :c
+     \\--- :b
+          \\--- :a (*)
+
+(*) - details omitted (listed previously)
+""")
+    }
+
+    def "cannot add a task with must run after induced circular reference that was previously in graph but not required"() {
+        Task a = createTask("a")
+        Task b = task("b", mustRunAfter: [a])
+        Task c = task("c", dependsOn: [b])
+        Task d = task("d", dependsOn: [c])
+        relationships(a, mustRunAfter: [c])
+        executionPlan.addToTaskGraph([d])
+
+        when:
+        executionPlan.addToTaskGraph([a])
+        executionPlan.determineExecutionPlan()
+
+        then:
+        def e = thrown CircularReferenceException
+        e.message == TextUtil.toPlatformLineSeparators("""Circular dependency between the following tasks:
+:a
+\\--- :c
+     \\--- :b
+          \\--- :a (*)
+
+(*) - details omitted (listed previously)
+""")
     }
 
     def "stops returning tasks on task execution failure"() {
         RuntimeException failure = new RuntimeException("failure");
         Task a = task("a");
         Task b = task("b");
-        executionPlan.addToTaskGraph([a, b])
+        addToGraphAndPopulate([a, b])
 
         when:
         def taskInfoA = taskToExecute
@@ -184,16 +401,16 @@ public class DefaultTaskExecutionPlanTest extends Specification {
     }
 
     protected TaskInfo getTaskToExecute() {
-        executionPlan.getTaskToExecute(anyTask)
+        executionPlan.getTaskToExecute()
     }
 
     def "stops returning tasks on first task failure when no failure handler provided"() {
         RuntimeException failure = new RuntimeException("failure");
-        Task a = brokenTask("a", failure);
+        Task a = task("a", failure: failure);
         Task b = task("b");
 
         when:
-        executionPlan.addToTaskGraph([a, b])
+        addToGraphAndPopulate([a, b])
 
         then:
         executedTasks == [a]
@@ -208,10 +425,10 @@ public class DefaultTaskExecutionPlanTest extends Specification {
 
     def "stops execution on task failure when failure handler indicates that execution should stop"() {
         RuntimeException failure = new RuntimeException("failure");
-        Task a = brokenTask("a", failure);
+        Task a = task("a", failure: failure);
         Task b = task("b");
 
-        executionPlan.addToTaskGraph([a, b])
+        addToGraphAndPopulate([a, b])
 
         TaskFailureHandler handler = Mock()
         RuntimeException wrappedFailure = new RuntimeException("wrapped");
@@ -235,16 +452,32 @@ public class DefaultTaskExecutionPlanTest extends Specification {
 
     def "continues to return tasks and rethrows failure on completion when failure handler indicates that execution should continue"() {
         RuntimeException failure = new RuntimeException();
-        Task a = brokenTask("a", failure);
+        Task a = task("a", failure: failure);
         Task b = task("b");
-        executionPlan.addToTaskGraph([a, b])
+        addToGraphAndPopulate([a, b])
 
-        TaskFailureHandler handler = Mock()
-        handler.onTaskFailure(a) >> {
-        }
+        when:
+        executionPlan.useFailureHandler(createIgnoreTaskFailureHandler(a));
+
+        then:
+        executedTasks == [a, b]
 
         when:
-        executionPlan.useFailureHandler(handler);
+        executionPlan.awaitCompletion()
+
+        then:
+        RuntimeException e = thrown()
+        e == failure
+    }
+
+    def "continues to return tasks when failure handler does not abort execution and task are mustRunAfter dependent"() {
+        RuntimeException failure = new RuntimeException();
+        Task a = task("a", failure: failure);
+        Task b = task("b", mustRunAfter: [a]);
+        addToGraphAndPopulate([a, b])
+
+        when:
+        executionPlan.useFailureHandler(createIgnoreTaskFailureHandler(a));
 
         then:
         executedTasks == [a, b]
@@ -259,18 +492,13 @@ public class DefaultTaskExecutionPlanTest extends Specification {
 
     def "does not attempt to execute tasks whose dependencies failed to execute"() {
         RuntimeException failure = new RuntimeException()
-        final Task a = brokenTask("a", failure)
-        final Task b = task("b", a)
+        final Task a = task("a", failure: failure)
+        final Task b = task("b", dependsOn: [a])
         final Task c = task("c")
-        executionPlan.addToTaskGraph([b, c])
-
-        TaskFailureHandler handler = Mock()
-        handler.onTaskFailure(a) >> {
-            // Ignore failure
-        }
+        addToGraphAndPopulate([b, c])
 
         when:
-        executionPlan.useFailureHandler(handler)
+        executionPlan.useFailureHandler(createIgnoreTaskFailureHandler(a))
 
         then:
         executedTasks == [a, c]
@@ -288,108 +516,206 @@ public class DefaultTaskExecutionPlanTest extends Specification {
         Task a = task("a");
 
         when:
-        executionPlan.addToTaskGraph(toList(a));
+        addToGraphAndPopulate(toList(a));
         executionPlan.clear()
 
         then:
-        executionPlan.getTasks() == []
+        executionPlan.tasks == []
         executedTasks == []
     }
 
-    def getExecutedTasks() {
-        def tasks = []
-        def taskInfo
-        while ((taskInfo = taskToExecute) != null) {
-            tasks << taskInfo.task
-            executionPlan.taskComplete(taskInfo)
-        }
-        return tasks
-    }
-
     def "can add additional tasks after execution and clear"() {
         given:
         Task a = task("a")
         Task b = task("b")
 
         when:
-        executionPlan.addToTaskGraph([a])
+        addToGraphAndPopulate([a])
 
         then:
-        executedTasks == [a]
+        executes(a)
 
         when:
         executionPlan.clear()
-        executionPlan.addToTaskGraph([b])
+        addToGraphAndPopulate([b])
 
         then:
-        executedTasks == [b]
+        executes(b)
     }
 
-    def "does not execute filtered tasks"() {
+    def "does not build graph for or execute filtered tasks"() {
         given:
-        Task a = task("a", task("a-dep"))
+        Task a = filteredTask("a")
         Task b = task("b")
+        Spec<Task> filter = Mock()
+
+        and:
+        filter.isSatisfiedBy(_) >> { Task t -> t != a }
 
         when:
-        executionPlan.useFilter({ it != a } as Spec<Task>);
-        executionPlan.addToTaskGraph([a, b])
+        executionPlan.useFilter(filter);
+        addToGraphAndPopulate([a, b])
 
         then:
-        executionPlan.getTasks() == [b]
-        executedTasks == [b]
+        executes(b)
     }
 
-    def "does not execute filtered dependencies"() {
+    def "does not build graph for or execute filtered dependencies"() {
         given:
-        Task a = task("a", task("a-dep"))
+        Task a = filteredTask("a")
         Task b = task("b")
-        Task c = task("c", a, b)
+        Task c = task("c", dependsOn: [a, b])
+        Spec<Task> filter = Mock()
+
+        and:
+        filter.isSatisfiedBy(_) >> { Task t -> t != a }
 
         when:
+        executionPlan.useFilter(filter)
+        addToGraphAndPopulate([c])
 
-        executionPlan.useFilter({ it != a } as Spec<Task>)
-        executionPlan.addToTaskGraph([c])
+        then:
+        executes(b, c)
+    }
+
+    def "does not build graph for or execute filtered tasks reachable via task ordering"() {
+        given:
+        Task a = filteredTask("a")
+        Task b = task("b", mustRunAfter: [a])
+        Task c = task("c", dependsOn: [a])
+        Spec<Task> filter = Mock()
+
+        and:
+        filter.isSatisfiedBy(_) >> { Task t -> t != a }
+
+        when:
+        executionPlan.useFilter(filter)
+        addToGraphAndPopulate([b, c])
 
         then:
-        executionPlan.tasks == [b, c]
-        executedTasks == [b, c]
+        executes(b, c)
     }
 
     def "will execute a task whose dependencies have been filtered"() {
         given:
-        Task b = task("b")
-        Task c = task("c", b)
+        Task b = filteredTask("b")
+        Task c = task("c", dependsOn: [b])
+        Spec<Task> filter = Mock()
+
+        and:
+        filter.isSatisfiedBy(_) >> { Task t -> t != b }
+
+        when:
+        executionPlan.useFilter(filter)
+        addToGraphAndPopulate([c]);
+
+        then:
+        executes(c)
+    }
+
+    def "one parallel task per project is allowed"() {
+        given:
+        //2 projects, 2 tasks each
+        def projectA = createChildProject(root, "a")
+        def projectB = createChildProject(root, "b")
+
+        def fooA = projectA.task("foo")
+        def barA = projectA.task("bar")
+
+        def fooB = projectB.task("foo")
+        def barB = projectB.task("bar")
+
+        addToGraphAndPopulate([fooA, barA, fooB, barB])
+
+        when:
+        def t1 = executionPlan.getTaskToExecute()
+        def t2 = executionPlan.getTaskToExecute()
+
+        then:
+        t1.task.project != t2.task.project
 
         when:
-        executionPlan.useFilter({ it != b } as Spec<Task>)
-        executionPlan.addToTaskGraph([c]);
+        executionPlan.taskComplete(t1)
+        executionPlan.taskComplete(t2)
+        def t3 = executionPlan.getTaskToExecute()
+        def t4 = executionPlan.getTaskToExecute()
 
         then:
-        executedTasks == [c]
+        t3.task.project != t4.task.project
     }
 
-    private void dependsOn(TaskInternal task, final Task... dependsOnTasks) {
-        TaskDependency taskDependency = Mock()
-        task.getTaskDependencies() >> taskDependency
-        taskDependency.getDependencies(task) >> toSet(dependsOnTasks)
+    void executes(Task... expectedTasks) {
+        assert executionPlan.tasks == expectedTasks as List
+        assert expectedTasks == expectedTasks as List
+    }
+
+    def getExecutedTasks() {
+        def tasks = []
+        def taskInfo
+        while ((taskInfo = taskToExecute) != null) {
+            tasks << taskInfo.task
+            executionPlan.taskComplete(taskInfo)
+        }
+        return tasks
+    }
+
+    private TaskDependency taskDependencyResolvingTo(TaskInternal task, List<Task> tasks) {
+        Mock(TaskDependency) {
+            getDependencies(task) >> tasks
+        }
+    }
+
+    private TaskDependency brokenDependencies() {
+        Mock(TaskDependency) {
+            0 * getDependencies(_)
+        }
+    }
+
+    private void dependsOn(TaskInternal task, List<Task> dependsOnTasks) {
+        task.getTaskDependencies() >> taskDependencyResolvingTo(task, dependsOnTasks)
     }
-    
-    private Task brokenTask(String name, final RuntimeException failure, final Task... dependsOnTasks) {
-        final TaskInternal task = createTask(name);
-        dependsOn(task, dependsOnTasks);
 
+    private void mustRunAfter(TaskInternal task, List<Task> mustRunAfterTasks) {
+        task.getMustRunAfter() >> taskDependencyResolvingTo(task, mustRunAfterTasks)
+    }
+
+    private void finalizedBy(TaskInternal task, List<Task> finalizedByTasks) {
+        task.getFinalizedBy() >> taskDependencyResolvingTo(task, finalizedByTasks)
+    }
+
+    private void failure(TaskInternal task, final RuntimeException failure) {
         task.state.getFailure() >> failure
         task.state.rethrowFailure() >> { throw failure }
-        return task;
     }
     
-    private TaskInternal task(final String name, final Task... dependsOnTasks) {
+    private TaskInternal task(final String name) {
+        task([:], name)
+    }
+
+    private TaskInternal task(Map options, final String name) {
+        def task = createTask(name)
+        relationships(options, task)
+        if (options.failure) {
+            failure(task, options.failure)
+        }
+        task.getDidWork() >> (options.containsKey('didWork') ? options.didWork : true)
+        return task
+    }
+
+    private void relationships(Map options, TaskInternal task) {
+        dependsOn(task, options.dependsOn ?: [])
+        mustRunAfter(task, options.mustRunAfter ?: [])
+        finalizedBy(task, options.finalizedBy ?: [])
+    }
+
+    private TaskInternal filteredTask(final String name) {
         def task = createTask(name);
-        dependsOn(task, dependsOnTasks);
-        task.state.getFailure() >> null
-        return task;
+        task.getTaskDependencies() >> brokenDependencies()
+        task.getMustRunAfter() >> brokenDependencies()
+        task.getFinalizedBy() >> taskDependencyResolvingTo(task, [])
+        return task
     }
-    
+
     private TaskInternal createTask(final String name) {
         TaskInternal task = Mock()
         TaskState state = Mock()
@@ -403,44 +729,5 @@ public class DefaultTaskExecutionPlanTest extends Specification {
         }
         return task;
     }
-
-    private class ExecuteTaskAction implements org.jmock.api.Action {
-        private final TaskInternal task;
-
-        public ExecuteTaskAction(TaskInternal task) {
-            this.task = task;
-        }
-
-        public Object invoke(Invocation invocation) throws Throwable {
-            executedTasks.add(task);
-            return null;
-        }
-
-        public void describeTo(Description description) {
-            description.appendText("execute task");
-        }
-    }
-
-    private static class DirectCacheAccess implements TaskArtifactStateCacheAccess {
-        public void useCache(String operationDisplayName, Runnable action) {
-            action.run();
-        }
-
-        public void longRunningOperation(String operationDisplayName, Runnable action) {
-            action.run();
-        }
-
-        public <K, V> PersistentIndexedCache createCache(String cacheName, Class<K> keyType, Class<V> valueType) {
-            throw new UnsupportedOperationException();
-        }
-
-        public <T> T useCache(String operationDisplayName, Factory<? extends T> action) {
-            throw new UnsupportedOperationException();
-        }
-
-        public <K, V> PersistentIndexedCache<K, V> createCache(String cacheName, Class<K> keyType, Class<V> valueType, Serializer<V> valueSerializer) {
-            throw new UnsupportedOperationException();
-        }
-    }
 }
 
diff --git a/subprojects/core/src/test/groovy/org/gradle/execution/taskgraph/DefaultTaskGraphExecuterTest.java b/subprojects/core/src/test/groovy/org/gradle/execution/taskgraph/DefaultTaskGraphExecuterTest.java
index 291bf8d..b673c42 100644
--- a/subprojects/core/src/test/groovy/org/gradle/execution/taskgraph/DefaultTaskGraphExecuterTest.java
+++ b/subprojects/core/src/test/groovy/org/gradle/execution/taskgraph/DefaultTaskGraphExecuterTest.java
@@ -22,8 +22,9 @@ import org.gradle.api.Task;
 import org.gradle.api.execution.TaskExecutionGraphListener;
 import org.gradle.api.execution.TaskExecutionListener;
 import org.gradle.api.internal.TaskInternal;
-import org.gradle.api.internal.changedetection.TaskArtifactStateCacheAccess;
+import org.gradle.api.internal.changedetection.state.TaskArtifactStateCacheAccess;
 import org.gradle.api.internal.project.ProjectInternal;
+import org.gradle.api.internal.tasks.DefaultTaskDependency;
 import org.gradle.api.internal.tasks.TaskStateInternal;
 import org.gradle.api.specs.Spec;
 import org.gradle.api.tasks.TaskDependency;
@@ -41,6 +42,7 @@ import org.jmock.Expectations;
 import org.jmock.api.Invocation;
 import org.jmock.integration.junit4.JMock;
 import org.jmock.integration.junit4.JUnit4Mockery;
+import org.jmock.lib.action.CustomAction;
 import org.junit.Before;
 import org.junit.Test;
 import org.junit.runner.RunWith;
@@ -48,21 +50,18 @@ import org.junit.runner.RunWith;
 import java.util.ArrayList;
 import java.util.List;
 
-import static org.gradle.util.HelperUtil.createRootProject;
-import static org.gradle.util.HelperUtil.toClosure;
+import static org.gradle.util.TestUtil.createRootProject;
+import static org.gradle.util.TestUtil.toClosure;
 import static org.gradle.util.WrapUtil.toList;
 import static org.gradle.util.WrapUtil.toSet;
 import static org.hamcrest.Matchers.*;
 import static org.junit.Assert.*;
 
-/**
- * @author Hans Dockter
- */
 @RunWith(JMock.class)
 public class DefaultTaskGraphExecuterTest {
-
-    JUnit4Mockery context = new JUnit4GroovyMockery();
-    private final ListenerManager listenerManager = context.mock(ListenerManager.class);
+    final JUnit4Mockery context = new JUnit4GroovyMockery();
+    final ListenerManager listenerManager = context.mock(ListenerManager.class);
+    final TaskArtifactStateCacheAccess taskArtifactStateCacheAccess = context.mock(TaskArtifactStateCacheAccess.class);
     DefaultTaskGraphExecuter taskExecuter;
     ProjectInternal root;
     List<Task> executedTasks = new ArrayList<Task>();
@@ -75,8 +74,16 @@ public class DefaultTaskGraphExecuterTest {
             will(returnValue(new ListenerBroadcast<TaskExecutionGraphListener>(TaskExecutionGraphListener.class)));
             one(listenerManager).createAnonymousBroadcaster(TaskExecutionListener.class);
             will(returnValue(new ListenerBroadcast<TaskExecutionListener>(TaskExecutionListener.class)));
+            allowing(taskArtifactStateCacheAccess).longRunningOperation(with(notNullValue(String.class)), with(notNullValue(Runnable.class)));
+            will(new CustomAction("run action") {
+                public Object invoke(Invocation invocation) throws Throwable {
+                    Runnable action = (Runnable) invocation.getParameter(1);
+                    action.run();
+                    return null;
+                }
+            });
         }});
-        taskExecuter = new org.gradle.execution.taskgraph.DefaultTaskGraphExecuter(listenerManager, new DefaultTaskPlanExecutor());
+        taskExecuter = new DefaultTaskGraphExecuter(listenerManager, new DefaultTaskPlanExecutor(taskArtifactStateCacheAccess));
     }
 
     @Test
@@ -244,8 +251,9 @@ public class DefaultTaskGraphExecuterTest {
         Task c = task("c", b);
         dependsOn(a, c);
 
+        taskExecuter.addTasks(toList(c));
         try {
-            taskExecuter.addTasks(toList(c));
+            taskExecuter.execute();
             fail();
         } catch (CircularReferenceException e) {
             // Expected
@@ -524,6 +532,12 @@ public class DefaultTaskGraphExecuterTest {
             will(returnValue(":" + name));
             allowing(task).getState();
             will(returnValue(state));
+            allowing(task).getMustRunAfter();
+            will(returnValue(new DefaultTaskDependency()));
+            allowing(task).getFinalizedBy();
+            will(returnValue(new DefaultTaskDependency()));
+            allowing(task).getDidWork();
+            will(returnValue(true));
             allowing(task).compareTo(with(notNullValue(TaskInternal.class)));
             will(new org.jmock.api.Action() {
                 public Object invoke(Invocation invocation) throws Throwable {
diff --git a/subprojects/core/src/test/groovy/org/gradle/execution/taskgraph/ParallelTaskExecutionPlanTest.groovy b/subprojects/core/src/test/groovy/org/gradle/execution/taskgraph/ParallelTaskExecutionPlanTest.groovy
deleted file mode 100644
index afcb0ee..0000000
--- a/subprojects/core/src/test/groovy/org/gradle/execution/taskgraph/ParallelTaskExecutionPlanTest.groovy
+++ /dev/null
@@ -1,26 +0,0 @@
-/*
- * Copyright 2013 the original author or authors.
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- *      http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package org.gradle.execution.taskgraph;
-
-
-public class ParallelTaskExecutionPlanTest extends DefaultTaskExecutionPlanTest {
-
-    protected TaskInfo getTaskToExecute() {
-        executionPlan.getTaskToExecute()
-    }
-}
-
diff --git a/subprojects/core/src/test/groovy/org/gradle/execution/taskgraph/TaskDependencyGraphTest.groovy b/subprojects/core/src/test/groovy/org/gradle/execution/taskgraph/TaskDependencyGraphTest.groovy
new file mode 100644
index 0000000..9f3c713
--- /dev/null
+++ b/subprojects/core/src/test/groovy/org/gradle/execution/taskgraph/TaskDependencyGraphTest.groovy
@@ -0,0 +1,77 @@
+/*
+ * Copyright 2013 the original author or authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.gradle.execution.taskgraph
+
+import org.gradle.api.internal.TaskInternal
+import spock.lang.Specification
+
+class TaskDependencyGraphTest extends Specification {
+    def graph = new TaskDependencyGraph()
+    def a = task('a')
+    def b = task('b')
+    def c = task('c')
+    def d = task('d')
+    def e = task('e')
+
+    private TaskInternal task(String name) {
+        Mock(TaskInternal) {
+            getName() >> name
+            compareTo(_) >> { args -> name.compareTo(args[0].name)}
+        }
+    }
+
+    void 'can create a node for a task'() {
+        when:
+        def node = graph.addNode(a)
+
+        then:
+        !node.inKnownState
+        node.hardPredecessors.empty
+        node.softSuccessors.empty
+        node.hardSuccessors.empty
+        node.finalizers.empty
+    }
+
+    void 'caches node for a given task'() {
+        when:
+        def node = graph.addNode(a)
+
+        then:
+        graph.getNode(a).is(node)
+        graph.addNode(a).is(node)
+    }
+
+    void 'can add multiple nodes'() {
+        when:
+        graph.addNode(a)
+        graph.addNode(b)
+
+        then:
+        graph.tasks == [a, b] as Set
+    }
+
+    void 'clear'() {
+        when:
+        graph.addNode(a)
+        graph.addNode(b)
+        graph.addNode(c)
+        graph.clear()
+
+        then:
+        !graph.tasks
+    }
+}
diff --git a/subprojects/core/src/test/groovy/org/gradle/execution/taskgraph/TaskPlanExecutorFactoryTest.groovy b/subprojects/core/src/test/groovy/org/gradle/execution/taskgraph/TaskPlanExecutorFactoryTest.groovy
index f7a921a..67cdb5c 100644
--- a/subprojects/core/src/test/groovy/org/gradle/execution/taskgraph/TaskPlanExecutorFactoryTest.groovy
+++ b/subprojects/core/src/test/groovy/org/gradle/execution/taskgraph/TaskPlanExecutorFactoryTest.groovy
@@ -17,7 +17,7 @@
 package org.gradle.execution.taskgraph;
 
 
-import org.gradle.api.internal.changedetection.TaskArtifactStateCacheAccess
+import org.gradle.api.internal.changedetection.state.TaskArtifactStateCacheAccess
 import spock.lang.Specification
 
 public class TaskPlanExecutorFactoryTest extends Specification {
diff --git a/subprojects/core/src/test/groovy/org/gradle/execution/taskpath/ProjectFinderByTaskPathTest.groovy b/subprojects/core/src/test/groovy/org/gradle/execution/taskpath/ProjectFinderByTaskPathTest.groovy
index 2120e88..eef22f6 100644
--- a/subprojects/core/src/test/groovy/org/gradle/execution/taskpath/ProjectFinderByTaskPathTest.groovy
+++ b/subprojects/core/src/test/groovy/org/gradle/execution/taskpath/ProjectFinderByTaskPathTest.groovy
@@ -17,20 +17,17 @@
 package org.gradle.execution.taskpath
 
 import org.gradle.api.internal.project.DefaultProject
-import org.gradle.util.HelperUtil
+import org.gradle.util.TestUtil
 import spock.lang.Specification
 
-/**
- * by Szczepan Faber, created at: 1/8/13
- */
 class ProjectFinderByTaskPathTest extends Specification {
 
     def finder = new ProjectFinderByTaskPath()
 
     //root->foo->bar
-    DefaultProject root = HelperUtil.createRootProject()
-    DefaultProject foo = HelperUtil.createChildProject(root, "foo")
-    DefaultProject bar = HelperUtil.createChildProject(foo, "bar")
+    DefaultProject root = TestUtil.createRootProject()
+    DefaultProject foo = TestUtil.createChildProject(root, "foo")
+    DefaultProject bar = TestUtil.createChildProject(foo, "bar")
 
     def "finds root"() {
         expect:
diff --git a/subprojects/core/src/test/groovy/org/gradle/execution/taskpath/ResolvedTaskPathTest.groovy b/subprojects/core/src/test/groovy/org/gradle/execution/taskpath/ResolvedTaskPathTest.groovy
index 6b65fa9..af61b10 100644
--- a/subprojects/core/src/test/groovy/org/gradle/execution/taskpath/ResolvedTaskPathTest.groovy
+++ b/subprojects/core/src/test/groovy/org/gradle/execution/taskpath/ResolvedTaskPathTest.groovy
@@ -19,9 +19,6 @@ package org.gradle.execution.taskpath
 import org.gradle.api.internal.project.ProjectInternal
 import spock.lang.Specification
 
-/**
- * by Szczepan Faber, created at: 1/29/13
- */
 class ResolvedTaskPathTest extends Specification {
 
     def "knows if path is qualified"() {
diff --git a/subprojects/core/src/test/groovy/org/gradle/execution/taskpath/TaskPathResolverTest.groovy b/subprojects/core/src/test/groovy/org/gradle/execution/taskpath/TaskPathResolverTest.groovy
index c2acb52..5628a0c 100644
--- a/subprojects/core/src/test/groovy/org/gradle/execution/taskpath/TaskPathResolverTest.groovy
+++ b/subprojects/core/src/test/groovy/org/gradle/execution/taskpath/TaskPathResolverTest.groovy
@@ -19,9 +19,6 @@ package org.gradle.execution.taskpath
 import org.gradle.api.internal.project.ProjectInternal
 import spock.lang.Specification
 
-/**
- * by Szczepan Faber, created at: 1/29/13
- */
 class TaskPathResolverTest extends Specification {
 
     private finder = Mock(ProjectFinderByTaskPath)
diff --git a/subprojects/core/src/test/groovy/org/gradle/groovy/scripts/DefaultScriptTest.groovy b/subprojects/core/src/test/groovy/org/gradle/groovy/scripts/DefaultScriptTest.groovy
index 0a32cd1..fe64d3e 100644
--- a/subprojects/core/src/test/groovy/org/gradle/groovy/scripts/DefaultScriptTest.groovy
+++ b/subprojects/core/src/test/groovy/org/gradle/groovy/scripts/DefaultScriptTest.groovy
@@ -21,19 +21,18 @@ package org.gradle.groovy.scripts
 import org.codehaus.groovy.control.CompilerConfiguration
 import org.gradle.api.initialization.dsl.ScriptHandler
 import org.gradle.api.internal.project.DefaultProject
-import org.gradle.internal.service.ServiceRegistry
 import org.gradle.api.logging.LoggingManager
+import org.gradle.internal.reflect.Instantiator
+import org.gradle.internal.service.ServiceRegistry
 import org.gradle.logging.StandardOutputCapture
-import org.gradle.util.HelperUtil
 import org.gradle.util.JUnit4GroovyMockery
+import org.gradle.util.TestUtil
 import org.jmock.integration.junit4.JMock
 import org.junit.Test
 import org.junit.runner.RunWith
-import static org.junit.Assert.*
 
-/**
- * @author Hans Dockter
- */
+import static org.junit.Assert.assertEquals
+
 @RunWith(JMock)
 class DefaultScriptTest {
     private final JUnit4GroovyMockery context = new JUnit4GroovyMockery()
@@ -47,10 +46,12 @@ class DefaultScriptTest {
             will(returnValue(context.mock(StandardOutputCapture.class)))
             allowing(serviceRegistryMock).get(LoggingManager.class)
             will(returnValue(context.mock(LoggingManager.class)))
+            allowing(serviceRegistryMock).get(Instantiator)
+            will(returnValue(context.mock(Instantiator)))
         }
 
         DefaultScript script = new GroovyShell(createBaseCompilerConfiguration()).parse(testScriptText)
-        DefaultProject testProject = HelperUtil.createRootProject()
+        DefaultProject testProject = TestUtil.createRootProject()
         testProject.custom = 'true'
         script.setScriptSource(new StringScriptSource('script', '//'))
         script.init(testProject, serviceRegistryMock)
diff --git a/subprojects/core/src/test/groovy/org/gradle/groovy/scripts/EmptyScript.java b/subprojects/core/src/test/groovy/org/gradle/groovy/scripts/EmptyScript.java
index 17a7b8e..d314f2d 100644
--- a/subprojects/core/src/test/groovy/org/gradle/groovy/scripts/EmptyScript.java
+++ b/subprojects/core/src/test/groovy/org/gradle/groovy/scripts/EmptyScript.java
@@ -15,9 +15,6 @@
  */
 package org.gradle.groovy.scripts;
 
-/**
- * @author Hans Dockter
- */
 public class EmptyScript extends groovy.lang.Script {
     public Object run() {
         return null;
diff --git a/subprojects/core/src/test/groovy/org/gradle/groovy/scripts/internal/DefaultScriptCompilationHandlerTest.java b/subprojects/core/src/test/groovy/org/gradle/groovy/scripts/internal/DefaultScriptCompilationHandlerTest.java
index d960ced..3ced662 100644
--- a/subprojects/core/src/test/groovy/org/gradle/groovy/scripts/internal/DefaultScriptCompilationHandlerTest.java
+++ b/subprojects/core/src/test/groovy/org/gradle/groovy/scripts/internal/DefaultScriptCompilationHandlerTest.java
@@ -50,9 +50,6 @@ import static org.gradle.util.Matchers.isA;
 import static org.hamcrest.Matchers.*;
 import static org.junit.Assert.*;
 
-/**
- * @author Hans Dockter
- */
 @RunWith(JMock.class)
 public class DefaultScriptCompilationHandlerTest {
 
diff --git a/subprojects/core/src/test/groovy/org/gradle/initialization/BuildFileProjectSpecTest.java b/subprojects/core/src/test/groovy/org/gradle/initialization/BuildFileProjectSpecTest.java
index 69ce214..3ce9934 100644
--- a/subprojects/core/src/test/groovy/org/gradle/initialization/BuildFileProjectSpecTest.java
+++ b/subprojects/core/src/test/groovy/org/gradle/initialization/BuildFileProjectSpecTest.java
@@ -16,7 +16,7 @@
 package org.gradle.initialization;
 
 import org.gradle.api.InvalidUserDataException;
-import org.gradle.api.internal.project.IProjectRegistry;
+import org.gradle.api.internal.project.ProjectRegistry;
 import org.gradle.api.internal.project.ProjectIdentifier;
 import org.gradle.test.fixtures.file.TestNameTestDirectoryProvider;
 import org.jmock.Expectations;
@@ -100,8 +100,8 @@ public class BuildFileProjectSpecTest {
         }
     }
 
-    private IProjectRegistry<ProjectIdentifier> registry(final ProjectIdentifier... projects) {
-        final IProjectRegistry<ProjectIdentifier> registry = context.mock(IProjectRegistry.class, String.valueOf(counter++));
+    private ProjectRegistry<ProjectIdentifier> registry(final ProjectIdentifier... projects) {
+        final ProjectRegistry<ProjectIdentifier> registry = context.mock(ProjectRegistry.class, String.valueOf(counter++));
         context.checking(new Expectations(){{
             allowing(registry).getAllProjects();
             will(returnValue(toSet(projects)));
diff --git a/subprojects/core/src/test/groovy/org/gradle/initialization/BuildLayoutParametersTest.groovy b/subprojects/core/src/test/groovy/org/gradle/initialization/BuildLayoutParametersTest.groovy
new file mode 100644
index 0000000..4954dc8
--- /dev/null
+++ b/subprojects/core/src/test/groovy/org/gradle/initialization/BuildLayoutParametersTest.groovy
@@ -0,0 +1,51 @@
+/*
+ * Copyright 2013 the original author or authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.gradle.initialization
+
+import org.gradle.StartParameter
+import org.gradle.internal.SystemProperties
+import org.gradle.test.fixtures.file.TestNameTestDirectoryProvider
+import org.gradle.util.SetSystemProperties
+import org.junit.Rule
+import spock.lang.Specification
+
+import static org.gradle.util.GFileUtils.canonicalise
+
+class BuildLayoutParametersTest extends Specification {
+
+    @Rule SetSystemProperties props = new SetSystemProperties()
+    @Rule TestNameTestDirectoryProvider temp = new TestNameTestDirectoryProvider()
+
+    def "has reasonable defaults"() {
+        expect:
+        def params = new BuildLayoutParameters()
+        params.searchUpwards
+        params.gradleUserHomeDir == canonicalise(StartParameter.DEFAULT_GRADLE_USER_HOME)
+        params.projectDir == canonicalise(SystemProperties.getCurrentDir())
+    }
+
+    def "reads gradle user home dir from system property"() {
+        def dir = temp.createDir("someGradleUserHomePath")
+        System.setProperty(StartParameter.GRADLE_USER_HOME_PROPERTY_KEY, dir.absolutePath)
+
+        when:
+        def params = new BuildLayoutParameters()
+
+        then:
+        params.gradleUserHomeDir == dir
+    }
+}
diff --git a/subprojects/core/src/test/groovy/org/gradle/initialization/BuildSourceBuilderTest.groovy b/subprojects/core/src/test/groovy/org/gradle/initialization/BuildSourceBuilderTest.groovy
deleted file mode 100644
index 8f86e87..0000000
--- a/subprojects/core/src/test/groovy/org/gradle/initialization/BuildSourceBuilderTest.groovy
+++ /dev/null
@@ -1,171 +0,0 @@
-/*
- * Copyright 2010 the original author or authors.
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- *      http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-package org.gradle.initialization
-
-import org.gradle.BuildResult
-import org.gradle.GradleLauncher
-import org.gradle.StartParameter
-import org.gradle.api.Project
-import org.gradle.api.file.FileCollection
-import org.gradle.api.internal.plugins.EmbeddableJavaProject
-import org.gradle.api.invocation.Gradle
-import org.gradle.api.plugins.Convention
-import org.gradle.cache.CacheBuilder
-import org.gradle.cache.CacheRepository
-import org.gradle.cache.DirectoryCacheBuilder
-import org.gradle.cache.PersistentCache
-import org.gradle.cache.internal.FileLockManager
-import org.gradle.test.fixtures.file.TestFile
-import org.gradle.test.fixtures.file.TestNameTestDirectoryProvider
-import org.gradle.util.JUnit4GroovyMockery
-import org.jmock.api.Action
-import org.junit.runner.RunWith
-import org.junit.*
-
-import static org.hamcrest.Matchers.*
-import static org.junit.Assert.assertEquals
-
-/**
- * @author Hans Dockter
- */
- at RunWith(org.jmock.integration.junit4.JMock)
- at Ignore //TODO SF spockify
-class BuildSourceBuilderTest {
-    @Rule public TestNameTestDirectoryProvider tmpDir = new TestNameTestDirectoryProvider();
-    JUnit4GroovyMockery context = new JUnit4GroovyMockery()
-    BuildSourceBuilder buildSourceBuilder
-    GradleLauncherFactory gradleFactoryMock = context.mock(GradleLauncherFactory.class)
-    GradleLauncher gradleMock = context.mock(GradleLauncher.class)
-    Project rootProjectMock = context.mock(Project.class)
-    FileCollection configurationMock = context.mock(FileCollection.class)
-    TestFile rootDir = tmpDir.createDir('root')
-    TestFile testBuildSrcDir = rootDir.file('buildSrc').createDir()
-    TestFile buildSrcCache = testBuildSrcDir.createDir(".gradle/noVersion/buildSrc");
-    List testDependencies
-    StartParameter expectedStartParameter
-    CacheRepository cacheRepository = context.mock(CacheRepository.class)
-    PersistentCache persistentCache = context.mock(PersistentCache.class)
-    BuildResult expectedBuildResult
-    Gradle build = context.mock(Gradle.class)
-    EmbeddableJavaProject projectMetaInfo = context.mock(EmbeddableJavaProject.class)
-
-    @Before public void setUp() {
-        buildSourceBuilder = new BuildSourceBuilder(gradleFactoryMock, context.mock(ClassLoaderRegistry.class), cacheRepository)
-        expectedStartParameter = new StartParameter(currentDir: testBuildSrcDir)
-        testDependencies = ['dep1' as File, 'dep2' as File]
-
-        Convention convention = context.mock(Convention)
-        context.checking {
-            allowing(build).getRootProject(); will(returnValue(rootProjectMock))
-            allowing(build).getStartParameter(); will(returnValue(expectedStartParameter))
-            allowing(rootProjectMock).getConvention(); will(returnValue(convention))
-            allowing(convention).getPlugin(EmbeddableJavaProject);
-            will(returnValue(projectMetaInfo))
-            allowing(projectMetaInfo).getRuntimeClasspath(); will(returnValue(configurationMock))
-            allowing(configurationMock).getFiles(); will(returnValue(testDependencies as Set))
-        }
-        expectedBuildResult = new BuildResult(build, null)
-    }
-
-    @Test public void testCreateClasspathWhenBuildSrcDirExistsAndHasNotBeenBuiltBefore() {
-        expectMarkerFileFetchedFromCache(false)
-        context.checking {
-            one(projectMetaInfo).getRebuildTasks(); will(returnValue(['clean', 'build']))
-            one(gradleFactoryMock).newInstance((StartParameter) withParam(notNullValue()));
-            will(returnValue(gradleMock))
-            one(gradleMock).addListener(withParam(not(nullValue()))); will(notifyProjectsEvaluated())
-            one(gradleMock).run(); will(returnValue(expectedBuildResult))
-        }
-        expectMarkerFileInCache()
-
-        createBuildFile()
-        def actualClasspath = buildSourceBuilder.createBuildSourceClasspath(expectedStartParameter).asFiles
-        assertEquals(testDependencies, actualClasspath)
-    }
-
-    @Test public void testCreateClasspathWhenBuildSrcDirExistsAndHasBeenBuiltBefore() {
-        expectMarkerFileFetchedFromCache(true)
-        context.checking {
-            one(projectMetaInfo).getBuildTasks(); will(returnValue(['build']))
-            one(gradleFactoryMock).newInstance((StartParameter) withParam(notNullValue()))
-            will(returnValue(gradleMock))
-            one(gradleMock).addListener(withParam(not(nullValue()))); will(notifyProjectsEvaluated())
-            one(gradleMock).run(); will(returnValue(expectedBuildResult))
-        }
-        expectMarkerFileInCache()
-
-        def actualClasspath = buildSourceBuilder.createBuildSourceClasspath(expectedStartParameter).asFiles
-        assertEquals(testDependencies, actualClasspath)
-    }
-
-    @Test public void testCreateClasspathWhenBuildSrcDirDoesNotExist() {
-        expectedStartParameter = expectedStartParameter.newInstance()
-        expectedStartParameter.setCurrentDir(new File('nonexisting'));
-        assertEquals([], buildSourceBuilder.createBuildSourceClasspath(expectedStartParameter).asFiles)
-    }
-
-    private expectMarkerFileFetchedFromCache(boolean markerFileExists) {
-        if (markerFileExists) {
-            buildSrcCache.createFile("built.bin");
-        } else {
-            new File(buildSrcCache, "built.bin").delete()
-        }
-        context.checking {
-            DirectoryCacheBuilder builder = context.mock(DirectoryCacheBuilder.class)
-            one(cacheRepository).cache('buildSrc')
-            will(returnValue(builder))
-
-            one(builder).forObject(testBuildSrcDir)
-            will(returnValue(builder))
-
-            one(builder).withLockMode(FileLockManager.LockMode.None)
-            will(returnValue(builder))
-
-            one(builder).withVersionStrategy(CacheBuilder.VersionStrategy.SharedCacheInvalidateOnVersionChange)
-            will(returnValue(builder))
-
-            one(builder).open()
-            will(returnValue(persistentCache))
-
-            one(persistentCache).getBaseDir()
-            will(returnValue(buildSrcCache))
-            one(persistentCache).useCache(withParam(equalTo("rebuild buildSrc")), (org.gradle.internal.Factory) withParam(any(org.gradle.internal.Factory.class)))
-            will(executeBuildSrcBuild())
-        }
-    }
-
-    private expectMarkerFileInCache() {
-        buildSrcCache.file("buildSrc.lock").exists()
-    }
-
-    private createBuildFile() {
-        new File(testBuildSrcDir, Project.DEFAULT_BUILD_FILE).createNewFile()
-    }
-
-    private Action executeBuildSrcBuild() {
-        return [invoke: {invocation -> invocation.getParameter(1).create()},
-                describeTo: {description -> }] as Action
-    }
-
-    private Action notifyProjectsEvaluated() {
-        return [invoke: {invocation -> invocation.getParameter(0).projectsEvaluated(build)},
-                describeTo: {description -> }] as Action
-    }
-
-    @After public void cleanup() {
-        buildSrcCache.file("buildSrc.lock").delete()
-    }
-}
diff --git a/subprojects/core/src/test/groovy/org/gradle/initialization/DefaultCommandLineConverterTest.java b/subprojects/core/src/test/groovy/org/gradle/initialization/DefaultCommandLineConverterTest.java
index 553cc21..231e265 100644
--- a/subprojects/core/src/test/groovy/org/gradle/initialization/DefaultCommandLineConverterTest.java
+++ b/subprojects/core/src/test/groovy/org/gradle/initialization/DefaultCommandLineConverterTest.java
@@ -37,9 +37,6 @@ import static org.hamcrest.Matchers.equalTo;
 import static org.junit.Assert.assertEquals;
 import static org.junit.Assert.assertThat;
 
-/**
- * @author Hans Dockter
- */
 public class DefaultCommandLineConverterTest {
     @Rule
     public TestNameTestDirectoryProvider testDir = new TestNameTestDirectoryProvider();
diff --git a/subprojects/core/src/test/groovy/org/gradle/initialization/DefaultGradleLauncherFactoryTest.groovy b/subprojects/core/src/test/groovy/org/gradle/initialization/DefaultGradleLauncherFactoryTest.groovy
index 0a4f694..001d241 100644
--- a/subprojects/core/src/test/groovy/org/gradle/initialization/DefaultGradleLauncherFactoryTest.groovy
+++ b/subprojects/core/src/test/groovy/org/gradle/initialization/DefaultGradleLauncherFactoryTest.groovy
@@ -17,12 +17,15 @@ package org.gradle.initialization
 
 import org.gradle.GradleLauncher
 import org.gradle.StartParameter
+import org.gradle.internal.service.scopes.GlobalScopeServices
 import org.gradle.cli.CommandLineConverter
+import org.gradle.internal.service.ServiceRegistry
 import spock.lang.Specification
 
 class DefaultGradleLauncherFactoryTest extends Specification {
     final CommandLineConverter<StartParameter> parameterConverter = Mock()
-    final DefaultGradleLauncherFactory factory = new DefaultGradleLauncherFactory();
+    final ServiceRegistry sharedServices = new GlobalScopeServices()
+    final DefaultGradleLauncherFactory factory = new DefaultGradleLauncherFactory(sharedServices)
 
     def setup() {
         factory.setCommandLineConverter(parameterConverter);
diff --git a/subprojects/core/src/test/groovy/org/gradle/initialization/DefaultGradleLauncherTest.java b/subprojects/core/src/test/groovy/org/gradle/initialization/DefaultGradleLauncherTest.java
index 3e42d2b..09edbe7 100644
--- a/subprojects/core/src/test/groovy/org/gradle/initialization/DefaultGradleLauncherTest.java
+++ b/subprojects/core/src/test/groovy/org/gradle/initialization/DefaultGradleLauncherTest.java
@@ -24,13 +24,15 @@ import org.gradle.api.initialization.ProjectDescriptor;
 import org.gradle.api.internal.ExceptionAnalyser;
 import org.gradle.api.internal.GradleInternal;
 import org.gradle.api.internal.SettingsInternal;
+import org.gradle.api.internal.file.BaseDirFileResolver;
 import org.gradle.api.internal.project.DefaultProject;
 import org.gradle.configuration.BuildConfigurer;
 import org.gradle.execution.BuildExecuter;
 import org.gradle.execution.TaskGraphExecuter;
+import org.gradle.internal.nativeplatform.filesystem.FileSystems;
 import org.gradle.logging.LoggingManagerInternal;
 import org.gradle.test.fixtures.file.TestNameTestDirectoryProvider;
-import org.gradle.util.HelperUtil;
+import org.gradle.util.TestUtil;
 import org.gradle.util.JUnit4GroovyMockery;
 import org.hamcrest.BaseMatcher;
 import org.hamcrest.Description;
@@ -47,9 +49,6 @@ import java.io.File;
 import static org.hamcrest.Matchers.*;
 import static org.junit.Assert.assertThat;
 
-/**
- * @author Hans Dockter
- */
 @RunWith(org.jmock.integration.junit4.JMock.class)
 public class DefaultGradleLauncherTest {
     private BuildLoader buildLoaderMock;
@@ -96,9 +95,10 @@ public class DefaultGradleLauncherTest {
         File expectedRootDir = tmpDir.file("rootDir");
         File expectedCurrentDir = new File(expectedRootDir, "currentDir");
 
-        expectedRootProjectDescriptor = new DefaultProjectDescriptor(null, "someName", new File("somedir"), new DefaultProjectDescriptorRegistry());
-        expectedRootProject = HelperUtil.createRootProject(expectedRootDir);
-        expectedCurrentProject = HelperUtil.createRootProject(expectedCurrentDir);
+        expectedRootProjectDescriptor = new DefaultProjectDescriptor(null, "someName", new File("somedir"), new DefaultProjectDescriptorRegistry(),
+                new BaseDirFileResolver(FileSystems.getDefault(), expectedRootDir));
+        expectedRootProject = TestUtil.createRootProject(expectedRootDir);
+        expectedCurrentProject = TestUtil.createRootProject(expectedCurrentDir);
 
         expectedStartParams = new StartParameter();
         expectedStartParams.setCurrentDir(expectedCurrentDir);
diff --git a/subprojects/core/src/test/groovy/org/gradle/initialization/DefaultGradlePropertiesLoaderTest.java b/subprojects/core/src/test/groovy/org/gradle/initialization/DefaultGradlePropertiesLoaderTest.java
index bc19764..0401418 100644
--- a/subprojects/core/src/test/groovy/org/gradle/initialization/DefaultGradlePropertiesLoaderTest.java
+++ b/subprojects/core/src/test/groovy/org/gradle/initialization/DefaultGradlePropertiesLoaderTest.java
@@ -32,9 +32,6 @@ import java.util.Properties;
 
 import static org.junit.Assert.*;
 
-/**
- * @author Hans Dockter
- */
 public class DefaultGradlePropertiesLoaderTest {
     private DefaultGradlePropertiesLoader gradlePropertiesLoader;
     private File gradleUserHomeDir;
diff --git a/subprojects/core/src/test/groovy/org/gradle/initialization/DefaultProjectDescriptorRegistryTest.java b/subprojects/core/src/test/groovy/org/gradle/initialization/DefaultProjectDescriptorRegistryTest.java
index c5f6a32..6755bd4 100644
--- a/subprojects/core/src/test/groovy/org/gradle/initialization/DefaultProjectDescriptorRegistryTest.java
+++ b/subprojects/core/src/test/groovy/org/gradle/initialization/DefaultProjectDescriptorRegistryTest.java
@@ -15,25 +15,28 @@
  */
 package org.gradle.initialization;
 
+import org.gradle.api.internal.file.BaseDirFileResolver;
+import org.gradle.api.internal.file.FileResolver;
+import org.gradle.internal.nativeplatform.filesystem.FileSystems;
 import org.gradle.util.Path;
 import org.junit.Test;
-import static org.junit.Assert.assertSame;
-import static org.junit.Assert.*;
-import static org.hamcrest.Matchers.*;
 
 import java.io.File;
 
-/**
- * @author Hans Dockter
- */
+import static org.hamcrest.Matchers.nullValue;
+import static org.hamcrest.Matchers.sameInstance;
+import static org.junit.Assert.assertSame;
+import static org.junit.Assert.assertThat;
+
 public class DefaultProjectDescriptorRegistryTest {
     private static final File TEST_DIR = new File("testDir");
 
+    private static final FileResolver FILE_RESOLVER = new BaseDirFileResolver(FileSystems.getDefault(), TEST_DIR.getAbsoluteFile());
     private final DefaultProjectDescriptorRegistry registry = new DefaultProjectDescriptorRegistry();
 
     @Test
     public void addProjectDescriptor() {
-        DefaultProjectDescriptor rootProject = new DefaultProjectDescriptor(null, "testName", TEST_DIR, registry);
+        DefaultProjectDescriptor rootProject = new DefaultProjectDescriptor(null, "testName", TEST_DIR, registry, FILE_RESOLVER);
 
         registry.addProject(rootProject);
         assertSame(rootProject, registry.getProject(rootProject.getPath()));
@@ -42,7 +45,7 @@ public class DefaultProjectDescriptorRegistryTest {
 
     @Test
     public void changeProjectDescriptorPath() {
-        DefaultProjectDescriptor project = new DefaultProjectDescriptor(null, "name", TEST_DIR, registry);
+        DefaultProjectDescriptor project = new DefaultProjectDescriptor(null, "name", TEST_DIR, registry, FILE_RESOLVER);
         registry.addProject(project);
 
         registry.changeDescriptorPath(Path.path(":"), Path.path(":newPath"));
diff --git a/subprojects/core/src/test/groovy/org/gradle/initialization/DefaultProjectDescriptorTest.java b/subprojects/core/src/test/groovy/org/gradle/initialization/DefaultProjectDescriptorTest.java
index 54c121d..d8b47c6 100644
--- a/subprojects/core/src/test/groovy/org/gradle/initialization/DefaultProjectDescriptorTest.java
+++ b/subprojects/core/src/test/groovy/org/gradle/initialization/DefaultProjectDescriptorTest.java
@@ -15,29 +15,30 @@
  */
 package org.gradle.initialization;
 
+import org.gradle.api.Project;
+import org.gradle.api.internal.file.BaseDirFileResolver;
+import org.gradle.api.internal.file.FileResolver;
+import org.gradle.internal.nativeplatform.filesystem.FileSystems;
+import org.gradle.internal.os.OperatingSystem;
 import org.gradle.util.Path;
+import org.jmock.Expectations;
+import org.jmock.integration.junit4.JUnit4Mockery;
 import org.junit.Before;
 import org.junit.Test;
 import org.junit.runner.RunWith;
-import static org.junit.Assert.assertSame;
-import static org.junit.Assert.assertEquals;
-import static org.junit.Assert.assertTrue;
-import org.gradle.api.Project;
-import org.jmock.integration.junit4.JUnit4Mockery;
-import org.jmock.Expectations;
 
 import java.io.File;
 import java.io.IOException;
 
-/**
- * @author Hans Dockter
- */
+import static org.junit.Assert.*;
+
 @RunWith(org.jmock.integration.junit4.JMock.class)
 public class DefaultProjectDescriptorTest {
     private DefaultProjectDescriptor projectDescriptor;
     private DefaultProjectDescriptor parentProjectDescriptor;
     private static final String TEST_NAME = "testName";
     private static final File TEST_DIR = new File("testDir");
+    private static final FileResolver FILE_RESOLVER = new BaseDirFileResolver(FileSystems.getDefault(), TEST_DIR.getAbsoluteFile());
     private DefaultProjectDescriptorRegistry testProjectDescriptorRegistry;
     private JUnit4Mockery context = new JUnit4Mockery();
 
@@ -45,9 +46,9 @@ public class DefaultProjectDescriptorTest {
     public void setUp() {
         testProjectDescriptorRegistry = new DefaultProjectDescriptorRegistry();
         parentProjectDescriptor = new DefaultProjectDescriptor(null, "somename", new File("somefile"),
-                testProjectDescriptorRegistry);
+                testProjectDescriptorRegistry, FILE_RESOLVER);
         projectDescriptor = new DefaultProjectDescriptor(parentProjectDescriptor, TEST_NAME, TEST_DIR,
-                testProjectDescriptorRegistry);
+                testProjectDescriptorRegistry, FILE_RESOLVER);
     }
 
     @Test
@@ -70,7 +71,7 @@ public class DefaultProjectDescriptorTest {
     @Test
     public void setName() {
         final String newName = "newName";
-        final IProjectDescriptorRegistry projectDescriptorRegistryMock = context.mock(IProjectDescriptorRegistry.class);
+        final ProjectDescriptorRegistry projectDescriptorRegistryMock = context.mock(ProjectDescriptorRegistry.class);
         projectDescriptor.setProjectDescriptorRegistry(projectDescriptorRegistryMock);
         context.checking(new Expectations() {{
             one(projectDescriptorRegistryMock).changeDescriptorPath(Path.path(TEST_NAME), Path.path(Project.PATH_SEPARATOR + newName));
@@ -80,6 +81,24 @@ public class DefaultProjectDescriptorTest {
     }
 
     @Test
+    public void setProjectDirRelative() {
+        final ProjectDescriptorRegistry projectDescriptorRegistryMock = context.mock(ProjectDescriptorRegistry.class);
+        projectDescriptor.setProjectDescriptorRegistry(projectDescriptorRegistryMock);
+        projectDescriptor.setProjectDir(new File("relative/path"));
+        final String expectedPath = new File(TEST_DIR, "relative/path").getAbsolutePath();
+        assertEquals(expectedPath, projectDescriptor.getProjectDir().getAbsolutePath());
+    }
+
+    @Test
+    public void setProjectDirAbsolute() {
+        final ProjectDescriptorRegistry projectDescriptorRegistryMock = context.mock(ProjectDescriptorRegistry.class);
+        projectDescriptor.setProjectDescriptorRegistry(projectDescriptorRegistryMock);
+        String absolutePath = OperatingSystem.current().isWindows() ? "C:\\absolute\\path" : "/absolute/path";
+        projectDescriptor.setProjectDir(new File(absolutePath));
+        assertEquals(absolutePath, projectDescriptor.getProjectDir().getAbsolutePath());
+    }
+
+    @Test
     public void buildFileIsBuiltFromBuildFileNameAndProjectDir() throws IOException {
         projectDescriptor.setBuildFileName("project.gradle");
         assertEquals(new File(TEST_DIR, "project.gradle").getCanonicalFile(), projectDescriptor.getBuildFile());
diff --git a/subprojects/core/src/test/groovy/org/gradle/initialization/DefaultSettingsTest.groovy b/subprojects/core/src/test/groovy/org/gradle/initialization/DefaultSettingsTest.groovy
index ff540a4..cda19f2 100644
--- a/subprojects/core/src/test/groovy/org/gradle/initialization/DefaultSettingsTest.groovy
+++ b/subprojects/core/src/test/groovy/org/gradle/initialization/DefaultSettingsTest.groovy
@@ -20,21 +20,24 @@ import org.gradle.StartParameter
 import org.gradle.api.Project
 import org.gradle.api.UnknownProjectException
 import org.gradle.api.initialization.ProjectDescriptor
+import org.gradle.api.initialization.Settings
+import org.gradle.api.internal.GradleInternal
+import org.gradle.api.internal.ThreadGlobalInstantiator
+import org.gradle.api.internal.file.FileResolver
+import org.gradle.api.plugins.PluginContainer
+import org.gradle.configuration.ScriptPluginFactory
 import org.gradle.groovy.scripts.ScriptSource
+import org.gradle.internal.service.scopes.ServiceRegistryFactory
 import org.gradle.util.JUnit4GroovyMockery
 import org.jmock.integration.junit4.JMock
 import org.jmock.lib.legacy.ClassImposteriser
 import org.junit.Before
 import org.junit.Test
 import org.junit.runner.RunWith
+
 import static org.junit.Assert.*
-import org.gradle.api.internal.GradleInternal
-import org.gradle.api.internal.ThreadGlobalInstantiator
 
-/**
- * @author Hans Dockter
- */
- at RunWith (JMock)
+ at RunWith(JMock)
 class DefaultSettingsTest {
     File settingsDir
     StartParameter startParameter
@@ -44,9 +47,14 @@ class DefaultSettingsTest {
     GradleInternal gradleMock
     DefaultSettings settings
     JUnit4GroovyMockery context = new JUnit4GroovyMockery()
-    DefaultProjectDescriptorRegistry projectDescriptorRegistry
-
-    @Before public void setUp() {
+    ProjectDescriptorRegistry projectDescriptorRegistry
+    ServiceRegistryFactory serviceRegistryFactory
+    PluginContainer pluginContainer
+    FileResolver fileResolver
+    ScriptPluginFactory scriptPluginFactory;
+
+    @Before
+    public void setUp() {
         context.setImposteriser(ClassImposteriser.INSTANCE)
         settingsDir = new File('/somepath/root').absoluteFile
         gradleProperties = [someGradleProp: 'someValue']
@@ -55,14 +63,32 @@ class DefaultSettingsTest {
 
         scriptSourceMock = context.mock(ScriptSource)
         gradleMock = context.mock(GradleInternal)
-
+        serviceRegistryFactory = context.mock(ServiceRegistryFactory.class)
+        pluginContainer = context.mock(PluginContainer.class)
+        scriptPluginFactory = context.mock(ScriptPluginFactory.class)
+        fileResolver = context.mock(FileResolver.class)
         projectDescriptorRegistry = new DefaultProjectDescriptorRegistry()
-        settings = ThreadGlobalInstantiator.orCreate.newInstance(DefaultSettings,
-                gradleMock, projectDescriptorRegistry, expectedClassLoader, settingsDir, scriptSourceMock, startParameter
-        )
-    }
 
-    @Test public void testSettings() {
+        ServiceRegistryFactory settingsServices = context.mock(ServiceRegistryFactory.class)
+        context.checking{
+                one(serviceRegistryFactory).createFor(with(any(Settings.class)));
+                will(returnValue(settingsServices));
+                one(settingsServices).get(PluginContainer.class);
+                will(returnValue(pluginContainer));
+                one(settingsServices).get(FileResolver.class);
+                will(returnValue(fileResolver));
+                one(settingsServices).get(ScriptPluginFactory.class);
+                will(returnValue(scriptPluginFactory));
+                one(settingsServices).get(ProjectDescriptorRegistry.class);
+                will(returnValue(projectDescriptorRegistry));
+        }
+        settings = ThreadGlobalInstantiator.orCreate.newInstance(DefaultSettings, serviceRegistryFactory,
+                    gradleMock, expectedClassLoader, settingsDir, scriptSourceMock, startParameter);
+
+    }
+
+    @Test
+    public void testSettings() {
         assert settings.startParameter.is(startParameter)
         assertSame(settings, settings.getSettings())
         assertEquals(settingsDir, settings.getSettingsDir())
@@ -74,7 +100,8 @@ class DefaultSettingsTest {
         assertSame(gradleMock, settings.gradle)
     }
 
-    @Test public void testInclude() {
+    @Test
+    public void testInclude() {
         ProjectDescriptor rootProjectDescriptor = settings.getRootProject();
         String projectA = "a"
         String projectB = "b"
@@ -90,7 +117,8 @@ class DefaultSettingsTest {
         testDescriptor(settings.project(":$projectB:$projectC"), projectC, new File(settingsDir, "$projectB/$projectC"))
     }
 
-    @Test public void testIncludeFlat() {
+    @Test
+    public void testIncludeFlat() {
         ProjectDescriptor rootProjectDescriptor = settings.getRootProject();
         String projectA = "a"
         String projectB = "b"
@@ -106,7 +134,8 @@ class DefaultSettingsTest {
         assertEquals(projectDir, descriptor.getProjectDir())
     }
 
-    @Test public void testCreateProjectDescriptor() {
+    @Test
+    public void testCreateProjectDescriptor() {
         String testName = "testname"
         File testDir = new File("testDir")
         DefaultProjectDescriptor projectDescriptor = settings.createProjectDescriptor(settings.getRootProject(), testName, testDir)
@@ -116,19 +145,22 @@ class DefaultSettingsTest {
         assertEquals(testDir.canonicalFile, projectDescriptor.getProjectDir())
     }
 
-    @Test public void testFindDescriptorByPath() {
+    @Test
+    public void testFindDescriptorByPath() {
         DefaultProjectDescriptor projectDescriptor = createTestDescriptor();
         DefaultProjectDescriptor foundProjectDescriptor = settings.project(projectDescriptor.getPath())
         assertSame(foundProjectDescriptor, projectDescriptor)
     }
 
-    @Test public void testFindDescriptorByProjectDir() {
+    @Test
+    public void testFindDescriptorByProjectDir() {
         DefaultProjectDescriptor projectDescriptor = createTestDescriptor()
         DefaultProjectDescriptor foundProjectDescriptor = settings.project(projectDescriptor.getProjectDir())
         assertSame(foundProjectDescriptor, projectDescriptor)
     }
 
-    @Test (expected = UnknownProjectException) public void testDescriptorByPath() {
+    @Test(expected = UnknownProjectException)
+    public void testDescriptorByPath() {
         DefaultProjectDescriptor projectDescriptor = createTestDescriptor()
         DefaultProjectDescriptor foundProjectDescriptor = settings.project(projectDescriptor.getPath())
         assertSame(foundProjectDescriptor, projectDescriptor)
@@ -136,7 +168,8 @@ class DefaultSettingsTest {
     }
 
 
-    @Test (expected = UnknownProjectException) public void testDescriptorByProjectDir() {
+    @Test(expected = UnknownProjectException)
+    public void testDescriptorByProjectDir() {
         DefaultProjectDescriptor projectDescriptor = createTestDescriptor()
         DefaultProjectDescriptor foundProjectDescriptor = settings.project(projectDescriptor.getProjectDir())
         assertSame(foundProjectDescriptor, projectDescriptor)
@@ -153,27 +186,32 @@ class DefaultSettingsTest {
         return [name: 'someName']
     }
 
-    @Test public void testCreateClassLoader() {
+    @Test
+    public void testCreateClassLoader() {
         StartParameter expectedStartParameter = settings.startParameter.newInstance()
         expectedStartParameter.setCurrentDir(new File(settingsDir, DefaultSettings.DEFAULT_BUILD_SRC_DIR))
-        URLClassLoader createdClassLoader = settings.getClassLoader()
+        def createdClassLoader = settings.getClassLoader()
         assertSame(createdClassLoader, expectedClassLoader)
     }
 
-    @Test public void testCanGetAndSetDynamicProperties() {
+    @Test
+    public void testCanGetAndSetDynamicProperties() {
         settings.dynamicProp = 'value'
         assertEquals('value', settings.dynamicProp)
     }
 
-    @Test (expected = MissingPropertyException) public void testPropertyMissing() {
+    @Test(expected = MissingPropertyException)
+    public void testPropertyMissing() {
         settings.unknownProp
     }
 
-    @Test public void testGetRootDir() {
+    @Test
+    public void testGetRootDir() {
         assertEquals(settingsDir, settings.rootDir);
     }
 
-    @Test public void testHasUsefulToString() {
+    @Test
+    public void testHasUsefulToString() {
         assertEquals('settings \'root\'', settings.toString())
     }
 }
diff --git a/subprojects/core/src/test/groovy/org/gradle/initialization/InstantiatingBuildLoaderTest.groovy b/subprojects/core/src/test/groovy/org/gradle/initialization/InstantiatingBuildLoaderTest.groovy
index 7a20c8f..4d37b1c 100644
--- a/subprojects/core/src/test/groovy/org/gradle/initialization/InstantiatingBuildLoaderTest.groovy
+++ b/subprojects/core/src/test/groovy/org/gradle/initialization/InstantiatingBuildLoaderTest.groovy
@@ -19,12 +19,14 @@ package org.gradle.initialization
 import org.gradle.StartParameter
 import org.gradle.api.initialization.ProjectDescriptor
 import org.gradle.api.internal.GradleInternal
+import org.gradle.api.internal.file.BaseDirFileResolver
 import org.gradle.api.internal.project.DefaultProject
 import org.gradle.api.internal.project.IProjectFactory
 import org.gradle.api.internal.project.ProjectInternal
+import org.gradle.internal.nativeplatform.filesystem.FileSystems
 import org.gradle.test.fixtures.file.TestNameTestDirectoryProvider
-import org.gradle.util.HelperUtil
 import org.gradle.util.JUnit4GroovyMockery
+import org.gradle.util.TestUtil
 import org.jmock.integration.junit4.JMock
 import org.junit.Before
 import org.junit.Rule
@@ -34,9 +36,6 @@ import org.junit.runner.RunWith
 import static org.hamcrest.Matchers.*
 import static org.junit.Assert.assertThat
 
-/**
- * @author Hans Dockter
- */
 @RunWith(JMock.class)
 class InstantiatingBuildLoaderTest {
 
@@ -45,7 +44,7 @@ class InstantiatingBuildLoaderTest {
     File testDir
     File rootProjectDir
     File childProjectDir
-    IProjectDescriptorRegistry projectDescriptorRegistry = new DefaultProjectDescriptorRegistry()
+    ProjectDescriptorRegistry projectDescriptorRegistry = new DefaultProjectDescriptorRegistry()
     StartParameter startParameter = new StartParameter()
     ProjectDescriptor rootDescriptor
     ProjectInternal rootProject
@@ -126,15 +125,15 @@ class InstantiatingBuildLoaderTest {
     }
 
     private ProjectDescriptor descriptor(String name, ProjectDescriptor parent, File projectDir) {
-        new DefaultProjectDescriptor(parent, name, projectDir, projectDescriptorRegistry)
+        new DefaultProjectDescriptor(parent, name, projectDir, projectDescriptorRegistry, new BaseDirFileResolver(FileSystems.default, rootProjectDir))
     }
 
     private ProjectInternal project(ProjectDescriptor descriptor, ProjectInternal parent) {
         DefaultProject project
         if (parent) {
-            project = HelperUtil.createChildProject(parent, descriptor.name, descriptor.projectDir)
+            project = TestUtil.createChildProject(parent, descriptor.name, descriptor.projectDir)
         } else {
-            project = HelperUtil.createRootProject(descriptor.projectDir)
+            project = TestUtil.createRootProject(descriptor.projectDir)
         }
         project
     }
diff --git a/subprojects/core/src/test/groovy/org/gradle/initialization/LayoutCommandLineConverterTest.groovy b/subprojects/core/src/test/groovy/org/gradle/initialization/LayoutCommandLineConverterTest.groovy
index 8d307e3..367bc65 100644
--- a/subprojects/core/src/test/groovy/org/gradle/initialization/LayoutCommandLineConverterTest.groovy
+++ b/subprojects/core/src/test/groovy/org/gradle/initialization/LayoutCommandLineConverterTest.groovy
@@ -16,14 +16,18 @@
 
 package org.gradle.initialization
 
+import org.gradle.StartParameter
+import org.gradle.internal.SystemProperties
+import org.gradle.test.fixtures.file.TestNameTestDirectoryProvider
+import org.junit.Rule
 import spock.lang.Specification
 
-/**
- * by Szczepan Faber, created at: 2/19/13
- */
+import static org.gradle.util.GFileUtils.canonicalise
+
 class LayoutCommandLineConverterTest extends Specification {
 
-    def converter = new LayoutCommandLineConverter();
+    def converter = new LayoutCommandLineConverter()
+    @Rule TestNameTestDirectoryProvider temp = new TestNameTestDirectoryProvider()
 
     def convert(String... args) {
         converter.convert(Arrays.asList(args))
@@ -31,9 +35,9 @@ class LayoutCommandLineConverterTest extends Specification {
 
     def "has reasonable defaults"() {
         expect:
-        convert().projectDir
-        !convert().gradleUserHomeDir
-        convert().searchUpwards == null
+        convert().projectDir == canonicalise(SystemProperties.getCurrentDir())
+        convert().gradleUserHomeDir == canonicalise(StartParameter.DEFAULT_GRADLE_USER_HOME)
+        convert().searchUpwards
     }
 
     def "converts"() {
@@ -42,4 +46,31 @@ class LayoutCommandLineConverterTest extends Specification {
         convert("-g", "bar").gradleUserHomeDir.name == "bar"
         !convert("-u").searchUpwards
     }
+
+    def "converts relatively to the target dir"() {
+        given:
+        def root = temp.createDir('root')
+        def target = new BuildLayoutParameters().setProjectDir(root)
+
+        when:
+        converter.convert(['-p', 'projectDir', '-g', 'gradleDir'], target)
+
+        then:
+        target.gradleUserHomeDir == temp.file("root/gradleDir")
+        target.projectDir == temp.file("root/projectDir")
+    }
+
+    def "converts absolute paths"() {
+        given:
+        def root = temp.createDir('root')
+        def other = temp.createDir('other')
+        def target = new BuildLayoutParameters().setProjectDir(root)
+
+        when:
+        converter.convert(['-p', other.file('projectDir').absolutePath, '-g', other.file('gradleDir').absolutePath], target)
+
+        then:
+        target.gradleUserHomeDir == temp.file("other/gradleDir")
+        target.projectDir == temp.file("other/projectDir")
+    }
 }
diff --git a/subprojects/core/src/test/groovy/org/gradle/initialization/ProjectDirectoryProjectSpecTest.java b/subprojects/core/src/test/groovy/org/gradle/initialization/ProjectDirectoryProjectSpecTest.java
index d2aae08..d6f2b1e 100644
--- a/subprojects/core/src/test/groovy/org/gradle/initialization/ProjectDirectoryProjectSpecTest.java
+++ b/subprojects/core/src/test/groovy/org/gradle/initialization/ProjectDirectoryProjectSpecTest.java
@@ -16,7 +16,7 @@
 package org.gradle.initialization;
 
 import org.gradle.api.InvalidUserDataException;
-import org.gradle.api.internal.project.IProjectRegistry;
+import org.gradle.api.internal.project.ProjectRegistry;
 import org.gradle.api.internal.project.ProjectIdentifier;
 import org.gradle.test.fixtures.file.TestNameTestDirectoryProvider;
 import org.gradle.util.GFileUtils;
@@ -101,8 +101,8 @@ public class ProjectDirectoryProjectSpecTest {
         }
     }
 
-    private IProjectRegistry<ProjectIdentifier> registry(final ProjectIdentifier... projects) {
-        final IProjectRegistry<ProjectIdentifier> registry = context.mock(IProjectRegistry.class, String.valueOf(counter++));
+    private ProjectRegistry<ProjectIdentifier> registry(final ProjectIdentifier... projects) {
+        final ProjectRegistry<ProjectIdentifier> registry = context.mock(ProjectRegistry.class, String.valueOf(counter++));
         context.checking(new Expectations(){{
             allowing(registry).getAllProjects();
             will(returnValue(toSet(projects)));
diff --git a/subprojects/core/src/test/groovy/org/gradle/initialization/ProjectPropertySettingBuildLoaderTest.groovy b/subprojects/core/src/test/groovy/org/gradle/initialization/ProjectPropertySettingBuildLoaderTest.groovy
index 76a3594..57fcd61 100644
--- a/subprojects/core/src/test/groovy/org/gradle/initialization/ProjectPropertySettingBuildLoaderTest.groovy
+++ b/subprojects/core/src/test/groovy/org/gradle/initialization/ProjectPropertySettingBuildLoaderTest.groovy
@@ -14,12 +14,11 @@
  * limitations under the License.
  */
 package org.gradle.initialization
-
 import org.gradle.api.Project
 import org.gradle.api.initialization.ProjectDescriptor
 import org.gradle.api.internal.GradleInternal
+import org.gradle.api.internal.plugins.ExtensionContainerInternal
 import org.gradle.api.internal.project.ProjectInternal
-import org.gradle.api.plugins.ExtensionContainer
 import org.gradle.api.plugins.ExtraPropertiesExtension
 import org.gradle.test.fixtures.file.TestNameTestDirectoryProvider
 import org.gradle.util.GUtil
@@ -37,9 +36,9 @@ class ProjectPropertySettingBuildLoaderTest extends Specification {
     final File rootProjectDir = tmpDir.createDir('root')
     final File childProjectDir = tmpDir.createDir('child')
     final ProjectPropertySettingBuildLoader loader = new ProjectPropertySettingBuildLoader(propertiesLoader, target)
-    final ExtensionContainer rootExtension = Mock()
+    final ExtensionContainerInternal rootExtension = Mock()
     final ExtraPropertiesExtension rootProperties = Mock()
-    final ExtensionContainer childExtension = Mock()
+    final ExtensionContainerInternal childExtension = Mock()
     final ExtraPropertiesExtension childProperties = Mock()
 
     def setup() {
diff --git a/subprojects/core/src/test/groovy/org/gradle/initialization/ScriptEvaluatingSettingsProcessorTest.groovy b/subprojects/core/src/test/groovy/org/gradle/initialization/ScriptEvaluatingSettingsProcessorTest.groovy
index 41c5c60..b0f7e5a 100644
--- a/subprojects/core/src/test/groovy/org/gradle/initialization/ScriptEvaluatingSettingsProcessorTest.groovy
+++ b/subprojects/core/src/test/groovy/org/gradle/initialization/ScriptEvaluatingSettingsProcessorTest.groovy
@@ -16,34 +16,30 @@
 
 package org.gradle.initialization
 
-import groovy.mock.interceptor.MockFor
 import org.gradle.StartParameter
 import org.gradle.api.internal.GradleInternal
+import org.gradle.api.internal.SettingsInternal
 import org.gradle.configuration.ScriptPlugin
 import org.gradle.configuration.ScriptPluginFactory
 import org.gradle.groovy.scripts.ScriptSource
 import org.gradle.util.JUnit4GroovyMockery
 import org.junit.Before
 import org.junit.Test
+
 import static org.junit.Assert.assertSame
 
-/**
- * @author Hans Dockter
- */
 class ScriptEvaluatingSettingsProcessorTest {
     static final File TEST_ROOT_DIR = new File('rootDir')
-    static final File TEST_CURRENT_DIR = new File('currentDir')
     ScriptEvaluatingSettingsProcessor settingsProcessor
     DefaultSettingsFinder expectedSettingsFinder
     SettingsFactory settingsFactory
     StartParameter expectedStartParameter
-    DefaultSettings expectedSettings
-    MockFor settingsFactoryMocker
+    SettingsInternal expectedSettings
     ScriptSource scriptSourceMock
     IGradlePropertiesLoader propertiesLoaderMock
     ScriptPluginFactory configurerFactoryMock
     Map expectedGradleProperties
-    URLClassLoader urlClassLoader
+    ClassLoader urlClassLoader
     GradleInternal gradleMock
     SettingsLocation settingsLocation
 
@@ -65,16 +61,14 @@ class ScriptEvaluatingSettingsProcessorTest {
     }
 
     private void initExpectedSettings() {
-        expectedSettings = new DefaultSettings()
-        DefaultProjectDescriptorRegistry projectDescriptorRegistry = new DefaultProjectDescriptorRegistry()
-        expectedSettings.setRootProjectDescriptor(new DefaultProjectDescriptor(null, TEST_ROOT_DIR.name,
-                TEST_ROOT_DIR, projectDescriptorRegistry))
-        expectedSettings.setProjectDescriptorRegistry(projectDescriptorRegistry)
-        expectedSettings.setStartParameter(expectedStartParameter)
+        expectedSettings = context.mock(SettingsInternal.class)
         context.checking {
             one(settingsFactory).createSettings(gradleMock, TEST_ROOT_DIR, scriptSourceMock, expectedGradleProperties, expectedStartParameter, urlClassLoader)
             will(returnValue(expectedSettings))
-            
+
+            allowing(expectedSettings).getClassLoader()
+            will(returnValue(urlClassLoader))
+
             one(settingsLocation).getSettingsDir()
             will(returnValue(TEST_ROOT_DIR))
             allowing(settingsLocation).getSettingsScriptSource()
diff --git a/subprojects/core/src/test/groovy/org/gradle/initialization/SettingsFactoryTest.java b/subprojects/core/src/test/groovy/org/gradle/initialization/SettingsFactoryTest.java
index f9a12fe..d437bc5 100644
--- a/subprojects/core/src/test/groovy/org/gradle/initialization/SettingsFactoryTest.java
+++ b/subprojects/core/src/test/groovy/org/gradle/initialization/SettingsFactoryTest.java
@@ -16,13 +16,19 @@
 package org.gradle.initialization;
 
 import org.gradle.StartParameter;
+import org.gradle.api.initialization.Settings;
 import org.gradle.api.internal.DynamicObjectAware;
 import org.gradle.api.internal.GradleInternal;
 import org.gradle.api.internal.ThreadGlobalInstantiator;
+import org.gradle.api.internal.file.FileResolver;
+import org.gradle.api.plugins.PluginContainer;
+import org.gradle.configuration.ScriptPluginFactory;
 import org.gradle.groovy.scripts.ScriptSource;
+import org.gradle.internal.service.scopes.ServiceRegistryFactory;
+import org.gradle.util.JUnit4GroovyMockery;
 import org.gradle.util.WrapUtil;
+import org.jmock.Expectations;
 import org.jmock.integration.junit4.JUnit4Mockery;
-import org.jmock.lib.legacy.ClassImposteriser;
 import org.junit.Test;
 import org.junit.runner.RunWith;
 
@@ -34,23 +40,40 @@ import java.util.Map;
 import static org.junit.Assert.assertEquals;
 import static org.junit.Assert.assertSame;
 
-/**
- * @author Hans Dockter
- */
 @RunWith(org.jmock.integration.junit4.JMock.class)
 public class SettingsFactoryTest {
-    private JUnit4Mockery context = new JUnit4Mockery() {{
-        setImposteriser(ClassImposteriser.INSTANCE);
-    }};
+    private JUnit4Mockery context = new JUnit4GroovyMockery();
 
     @Test
     public void createSettings() {
         final File expectedSettingsDir = new File("settingsDir");
         ScriptSource expectedScriptSource = context.mock(ScriptSource.class);
         Map<String, String> expectedGradleProperties = WrapUtil.toMap("key", "myvalue");
-        IProjectDescriptorRegistry expectedProjectDescriptorRegistry = new DefaultProjectDescriptorRegistry();
         StartParameter expectedStartParameter = new StartParameter();
-        SettingsFactory settingsFactory = new SettingsFactory(expectedProjectDescriptorRegistry, ThreadGlobalInstantiator.getOrCreate());
+        final ServiceRegistryFactory serviceRegistryFactory = context.mock(ServiceRegistryFactory.class);
+        final ServiceRegistryFactory settingsInternallServiceRegistry = context.mock(ServiceRegistryFactory.class);
+        final PluginContainer pluginContainer = context.mock(PluginContainer.class);
+        final FileResolver fileResolver = context.mock(FileResolver.class);
+        final ScriptPluginFactory scriptPluginFactory = context.mock(ScriptPluginFactory.class);
+
+        final ProjectDescriptorRegistry expectedProjectDescriptorRegistry = context.mock(ProjectDescriptorRegistry.class);
+
+        context.checking(new Expectations() {{
+            one(serviceRegistryFactory).createFor(with(any(Settings.class)));
+            will(returnValue(settingsInternallServiceRegistry));
+            one(settingsInternallServiceRegistry).get(PluginContainer.class);
+            will(returnValue(pluginContainer));
+            one(settingsInternallServiceRegistry).get(FileResolver.class);
+            will(returnValue(fileResolver));
+            one(settingsInternallServiceRegistry).get(ScriptPluginFactory.class);
+            will(returnValue(scriptPluginFactory));
+            one(settingsInternallServiceRegistry).get(ProjectDescriptorRegistry.class);
+            will(returnValue(expectedProjectDescriptorRegistry));
+            one(expectedProjectDescriptorRegistry).addProject(with(any(DefaultProjectDescriptor.class)));
+        }});
+
+
+        SettingsFactory settingsFactory = new SettingsFactory(ThreadGlobalInstantiator.getOrCreate(), serviceRegistryFactory);
         final URLClassLoader urlClassLoader = new URLClassLoader(new URL[0]);
         GradleInternal gradle = context.mock(GradleInternal.class);
 
@@ -60,7 +83,7 @@ public class SettingsFactoryTest {
         assertSame(gradle, settings.getGradle());
         assertSame(expectedProjectDescriptorRegistry, settings.getProjectDescriptorRegistry());
         for (Map.Entry<String, String> entry : expectedGradleProperties.entrySet()) {
-            assertEquals(entry.getValue(), ((DynamicObjectAware)settings).getAsDynamicObject().getProperty(entry.getKey()));
+            assertEquals(entry.getValue(), ((DynamicObjectAware) settings).getAsDynamicObject().getProperty(entry.getKey()));
         }
 
         assertSame(expectedSettingsDir, settings.getSettingsDir());
diff --git a/subprojects/core/src/test/groovy/org/gradle/initialization/SettingsHandlerTest.java b/subprojects/core/src/test/groovy/org/gradle/initialization/SettingsHandlerTest.java
index c50b8dc..0c83cba 100644
--- a/subprojects/core/src/test/groovy/org/gradle/initialization/SettingsHandlerTest.java
+++ b/subprojects/core/src/test/groovy/org/gradle/initialization/SettingsHandlerTest.java
@@ -18,9 +18,11 @@ package org.gradle.initialization;
 import org.gradle.StartParameter;
 import org.gradle.api.internal.GradleInternal;
 import org.gradle.api.internal.SettingsInternal;
-import org.gradle.api.internal.project.IProjectRegistry;
+import org.gradle.api.internal.project.ProjectRegistry;
+import org.gradle.internal.service.scopes.ServiceRegistryFactory;
+import org.gradle.initialization.buildsrc.BuildSourceBuilder;
+import org.gradle.invocation.BuildClassLoaderRegistry;
 import org.gradle.util.GFileUtils;
-import org.gradle.util.MultiParentClassLoader;
 import org.gradle.util.WrapUtil;
 import org.hamcrest.Description;
 import org.hamcrest.Factory;
@@ -38,9 +40,6 @@ import java.net.URLClassLoader;
 import static org.hamcrest.Matchers.sameInstance;
 import static org.junit.Assert.assertThat;
 
-/**
- * @author Hans Dockter
- */
 public class SettingsHandlerTest {
     private JUnit4Mockery context = new JUnit4Mockery() {{
         setImposteriser(ClassImposteriser.INSTANCE);
@@ -53,7 +52,6 @@ public class SettingsHandlerTest {
     private ISettingsFinder settingsFinder = context.mock(ISettingsFinder.class);
     private SettingsProcessor settingsProcessor = context.mock(SettingsProcessor.class);
     private BuildSourceBuilder buildSourceBuilder = context.mock(BuildSourceBuilder.class);
-    private MultiParentClassLoader scriptClassLoader = context.mock(MultiParentClassLoader.class);
     private SettingsHandler settingsHandler = new SettingsHandler(settingsFinder, settingsProcessor,
             buildSourceBuilder);
 
@@ -69,8 +67,10 @@ public class SettingsHandlerTest {
     }
 
     private void prepareForExistingSettings() {
-        final IProjectRegistry projectRegistry = context.mock(IProjectRegistry.class);
+        final ProjectRegistry projectRegistry = context.mock(ProjectRegistry.class);
         final DefaultProjectDescriptor projectDescriptor = context.mock(DefaultProjectDescriptor.class);
+        final ServiceRegistryFactory services = context.mock(ServiceRegistryFactory.class);
+        final BuildClassLoaderRegistry classLoaderRegistry = context.mock(BuildClassLoaderRegistry.class);
         startParameter.setCurrentDir(settingsLocation.getSettingsDir());
 
         context.checking(new Expectations() {{
@@ -89,19 +89,22 @@ public class SettingsHandlerTest {
             allowing(settings).getClassLoader();
             will(returnValue(urlClassLoader));
 
-            allowing(gradle).getScriptClassLoader();
-            will(returnValue(scriptClassLoader));
+            allowing(services).get(BuildClassLoaderRegistry.class);
+            will(returnValue(classLoaderRegistry));
 
             allowing(gradle).getStartParameter();
             will(returnValue(startParameter));
 
+            allowing(gradle).getServices();
+            will(returnValue(services));
+
             allowing(settingsFinder).find(startParameter);
             will(returnValue(settingsLocation));
 
             one(settingsProcessor).process(gradle, settingsLocation, urlClassLoader, startParameter);
             will(returnValue(settings));
 
-            one(scriptClassLoader).addParent(urlClassLoader);
+            one(classLoaderRegistry).addRootClassLoader(urlClassLoader);
         }});
     }
 
diff --git a/subprojects/core/src/test/groovy/org/gradle/initialization/buildsrc/BuildSourceBuilderTest.groovy b/subprojects/core/src/test/groovy/org/gradle/initialization/buildsrc/BuildSourceBuilderTest.groovy
new file mode 100644
index 0000000..75925ad
--- /dev/null
+++ b/subprojects/core/src/test/groovy/org/gradle/initialization/buildsrc/BuildSourceBuilderTest.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.initialization.buildsrc
+
+import org.gradle.StartParameter
+import org.gradle.cache.CacheRepository
+import org.gradle.cache.PersistentCache
+import org.gradle.initialization.ClassLoaderRegistry
+import org.gradle.initialization.GradleLauncherFactory
+import org.gradle.internal.classpath.ClassPath
+import org.gradle.test.fixtures.file.TestNameTestDirectoryProvider
+import org.junit.Rule
+import spock.lang.Specification
+
+class BuildSourceBuilderTest extends Specification {
+
+    @Rule TestNameTestDirectoryProvider tmpDir = new TestNameTestDirectoryProvider()
+
+    GradleLauncherFactory launcherFactory = Mock()
+    ClassLoaderRegistry loaderRegistry = Mock()
+    CacheRepository cacheRepository = Mock()
+    BuildSourceBuilder buildSourceBuilder = Spy(BuildSourceBuilder, constructorArgs: [launcherFactory, loaderRegistry,  cacheRepository])
+
+    StartParameter parameter = new StartParameter()
+
+    void "creates classpath when build src does not exist"() {
+        when:
+        parameter.setCurrentDir(new File('nonexisting'));
+        then:
+        buildSourceBuilder.createBuildSourceClasspath(parameter).asFiles == []
+    }
+
+    void "creates classpath when build src exists"() {
+        def cache = Mock(PersistentCache)
+        def classpath = Mock(ClassPath)
+        buildSourceBuilder.createCache(parameter) >> cache
+        cache.useCache(_ as String, _ as BuildSrcUpdateFactory) >> classpath
+
+        when:
+        parameter.setCurrentDir(tmpDir.createDir("someDir"));
+
+        then:
+        buildSourceBuilder.createBuildSourceClasspath(parameter) == classpath
+    }
+}
diff --git a/subprojects/core/src/test/groovy/org/gradle/initialization/buildsrc/BuildSrcBuildListenerFactoryTest.groovy b/subprojects/core/src/test/groovy/org/gradle/initialization/buildsrc/BuildSrcBuildListenerFactoryTest.groovy
new file mode 100644
index 0000000..8e0c913
--- /dev/null
+++ b/subprojects/core/src/test/groovy/org/gradle/initialization/buildsrc/BuildSrcBuildListenerFactoryTest.groovy
@@ -0,0 +1,62 @@
+/*
+ * Copyright 2013 the original author or authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS 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.buildsrc
+
+import spock.lang.Specification
+import org.gradle.api.internal.GradleInternal
+import org.gradle.api.internal.project.ProjectInternal
+import org.gradle.api.plugins.Convention
+import org.gradle.api.internal.plugins.EmbeddableJavaProject
+import org.gradle.StartParameter
+
+class BuildSrcBuildListenerFactoryTest extends Specification {
+
+    def startParameter = Mock(StartParameter)
+    def plugin = Stub(EmbeddableJavaProject)
+    def convention = Mock(Convention) {
+        getPlugin(EmbeddableJavaProject) >> plugin
+    }
+    def project = Mock(ProjectInternal) {
+        getConvention() >> convention
+    }
+    def gradle = Mock(GradleInternal) {
+        getStartParameter() >> startParameter
+        getRootProject() >> project
+    }
+
+    def "configures task names when rebuild on"() {
+        def listener = new BuildSrcBuildListenerFactory().create(true)
+        plugin.getRebuildTasks() >> ['fooBuild']
+
+        when:
+        listener.onConfigure(gradle)
+
+        then:
+        1 * startParameter.setTaskNames(['fooBuild'])
+    }
+
+    def "configures task names when rebuild off"() {
+        def listener = new BuildSrcBuildListenerFactory().create(false)
+        plugin.getBuildTasks() >> ['barBuild']
+
+        when:
+        listener.onConfigure(gradle)
+
+        then:
+        1 * startParameter.setTaskNames(['barBuild'])
+    }
+}
diff --git a/subprojects/core/src/test/groovy/org/gradle/initialization/buildsrc/BuildSrcUpdateFactoryTest.groovy b/subprojects/core/src/test/groovy/org/gradle/initialization/buildsrc/BuildSrcUpdateFactoryTest.groovy
new file mode 100644
index 0000000..ce52264
--- /dev/null
+++ b/subprojects/core/src/test/groovy/org/gradle/initialization/buildsrc/BuildSrcUpdateFactoryTest.groovy
@@ -0,0 +1,67 @@
+/*
+ * Copyright 2013 the original author or authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS 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.buildsrc
+
+import org.gradle.GradleLauncher
+import org.gradle.cache.PersistentCache
+import org.gradle.test.fixtures.file.TestNameTestDirectoryProvider
+import org.junit.Rule
+import spock.lang.Specification
+
+class BuildSrcUpdateFactoryTest extends Specification {
+
+    @Rule TestNameTestDirectoryProvider temp = new TestNameTestDirectoryProvider()
+
+    def cache = Stub(PersistentCache)
+    def launcher = Stub(GradleLauncher)
+    def listener = Stub(BuildSrcBuildListenerFactory.Listener)
+    def listenerFactory = Mock(BuildSrcBuildListenerFactory)
+    def factory = new BuildSrcUpdateFactory(cache, launcher, listenerFactory)
+
+    def "creates classpath"() {
+        cache.getBaseDir() >> temp.testDirectory
+        listener.getRuntimeClasspath() >> [new File("dummy")]
+
+        when:
+        def classpath = factory.create()
+
+        then:
+        classpath.asFiles == [new File("dummy")]
+        1 * listenerFactory.create(_) >> listener
+    }
+
+    def "uses listener with rebuild off when marker file present"() {
+        temp.createFile("built.bin")
+        cache.getBaseDir() >> temp.testDirectory
+
+        when:
+        factory.create()
+
+        then:
+        1 * listenerFactory.create(false) >> listener
+    }
+
+    def "uses listener with rebuild on when marker file not present"() {
+        cache.getBaseDir() >> temp.createDir("empty")
+
+        when:
+        factory.create()
+
+        then:
+        1 * listenerFactory.create(true) >> listener
+    }
+}
diff --git a/subprojects/core/src/test/groovy/org/gradle/internal/featurelifecycle/DeprecatedFeatureUsageTest.groovy b/subprojects/core/src/test/groovy/org/gradle/internal/featurelifecycle/DeprecatedFeatureUsageTest.groovy
new file mode 100644
index 0000000..d642ee7
--- /dev/null
+++ b/subprojects/core/src/test/groovy/org/gradle/internal/featurelifecycle/DeprecatedFeatureUsageTest.groovy
@@ -0,0 +1,38 @@
+/*
+ * Copyright 2013 the original author or authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.gradle.internal.featurelifecycle
+
+import spock.lang.Specification
+
+class DeprecatedFeatureUsageTest extends Specification {
+    def "can create copy with stack-trace filled in"() {
+        def usage = new DeprecatedFeatureUsage("message", DeprecatedFeatureUsageTest)
+
+        expect:
+        usage.stack.empty
+        def copy = usage.withStackTrace()
+        copy.message == usage.message
+        !copy.stack.empty
+    }
+
+    def "returns self when stack-trace already filled in"() {
+        def usage = new DeprecatedFeatureUsage("message", DeprecatedFeatureUsageTest).withStackTrace()
+
+        expect:
+        usage.withStackTrace() == usage
+    }
+}
diff --git a/subprojects/core/src/test/groovy/org/gradle/internal/featurelifecycle/LoggingDeprecatedFeatureHandlerTest.groovy b/subprojects/core/src/test/groovy/org/gradle/internal/featurelifecycle/LoggingDeprecatedFeatureHandlerTest.groovy
new file mode 100644
index 0000000..353eaa6
--- /dev/null
+++ b/subprojects/core/src/test/groovy/org/gradle/internal/featurelifecycle/LoggingDeprecatedFeatureHandlerTest.groovy
@@ -0,0 +1,57 @@
+/*
+ * Copyright 2013 the original author or authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.gradle.internal.featurelifecycle
+
+import org.gradle.logging.ConfigureLogging
+import org.gradle.logging.TestAppender
+import org.gradle.util.SetSystemProperties
+import org.gradle.util.TextUtil
+import org.junit.Rule
+import spock.lang.Specification
+
+class LoggingDeprecatedFeatureHandlerTest extends Specification {
+    final appender = new TestAppender()
+    @Rule final ConfigureLogging logging = new ConfigureLogging(appender)
+    @Rule SetSystemProperties systemProperties = new SetSystemProperties()
+    final locationReporter = Mock(UsageLocationReporter)
+    final handler = new LoggingDeprecatedFeatureHandler(locationReporter)
+
+    def "logs each deprecation warning once only"() {
+        when:
+        handler.deprecatedFeatureUsed(new DeprecatedFeatureUsage("feature1", LoggingDeprecatedFeatureHandlerTest))
+        handler.deprecatedFeatureUsed(new DeprecatedFeatureUsage("feature2", LoggingDeprecatedFeatureHandlerTest))
+        handler.deprecatedFeatureUsed(new DeprecatedFeatureUsage("feature2", LoggingDeprecatedFeatureHandlerTest))
+
+        then:
+        appender.toString() == '[WARN feature1][WARN feature2]'
+    }
+
+    def "location reporter can prepend text"() {
+        def usage = new DeprecatedFeatureUsage("feature", LoggingDeprecatedFeatureHandlerTest)
+
+        when:
+        handler.deprecatedFeatureUsed(usage)
+
+        then:
+        1 * locationReporter.reportLocation(_, _) >> { DeprecatedFeatureUsage param1, StringBuilder message ->
+            message.append("location")
+        }
+
+        and:
+        appender.toString() == TextUtil.toPlatformLineSeparators('[WARN location\nfeature]')
+    }
+}
diff --git a/subprojects/core/src/test/groovy/org/gradle/internal/featurelifecycle/ScriptUsageLocationReporterTest.groovy b/subprojects/core/src/test/groovy/org/gradle/internal/featurelifecycle/ScriptUsageLocationReporterTest.groovy
new file mode 100644
index 0000000..8613b91
--- /dev/null
+++ b/subprojects/core/src/test/groovy/org/gradle/internal/featurelifecycle/ScriptUsageLocationReporterTest.groovy
@@ -0,0 +1,126 @@
+/*
+ * Copyright 2013 the original author or authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.gradle.internal.featurelifecycle
+
+import org.gradle.groovy.scripts.ScriptSource
+import spock.lang.Specification
+import org.gradle.groovy.scripts.Script
+
+class ScriptUsageLocationReporterTest extends Specification {
+    final reporter = new ScriptUsageLocationReporter()
+
+    def "does nothing when no caller is a script"() {
+        def stack = [
+                new StackTraceElement("SomeClass", "method", "SomeClass.java", 12),
+                new StackTraceElement("SomeClass", "method", "SomeClass.java", 77)]
+        def usage = Stub(DeprecatedFeatureUsage) {
+            getStack() >> stack
+        }
+        def result = new StringBuilder()
+
+        when:
+        reporter.reportLocation(usage, result)
+
+        then:
+        result.length() == 0
+    }
+
+    def "reports location when immediate caller is a script"() {
+        def scriptSource = Stub(ScriptSource) {
+            getFileName() >> "some-file.gradle"
+            getDisplayName() >> "build script 'some-file.gradle'"
+        }
+        def script = Stub(Script) {
+            getScriptSource() >> scriptSource
+        }
+        def stack = [
+                new StackTraceElement("SomeClass", "method", "some-file.gradle", 12),
+                new StackTraceElement("SomeClass", "method", "some-file.gradle", 77)]
+        def usage = Stub(DeprecatedFeatureUsage) {
+            getStack() >> stack
+        }
+        def result = new StringBuilder()
+
+        given:
+        reporter.beforeScript(script)
+        reporter.afterScript(script, null)
+
+        when:
+        reporter.reportLocation(usage, result)
+
+        then:
+        result.toString() == /Build script 'some-file.gradle': line 12/
+    }
+
+    def "reports location when second caller is a script"() {
+        def scriptSource = Stub(ScriptSource) {
+            getFileName() >> "some-file.gradle"
+            getDisplayName() >> "build script 'some-file.gradle'"
+        }
+        def script = Stub(Script) {
+            getScriptSource() >> scriptSource
+        }
+        def stack = [
+                new StackTraceElement("SomeLibrary", "method", "SomeLibrary.java", 103),
+                new StackTraceElement("SomeLibrary", "method", "SomeLibrary.java", 67),
+                new StackTraceElement("SomeClass", "method", "some-file.gradle", 12)
+                ]
+        def usage = Stub(DeprecatedFeatureUsage) {
+            getStack() >> stack
+        }
+        def result = new StringBuilder()
+
+        given:
+        reporter.beforeScript(script)
+        reporter.afterScript(script, null)
+
+        when:
+        reporter.reportLocation(usage, result)
+
+        then:
+        result.toString() == /Build script 'some-file.gradle': line 12/
+    }
+
+    def "does not report location when subsequent caller is a script"() {
+        def scriptSource = Stub(ScriptSource) {
+            getFileName() >> "some-file.gradle"
+            getDisplayName() >> "build script 'some-file.gradle'"
+        }
+        def script = Stub(Script) {
+            getScriptSource() >> scriptSource
+        }
+        def stack = [
+                new StackTraceElement("SomeLibrary", "method", "SomeLibrary.java", 103),
+                new StackTraceElement("OtherLibrary", "method", "OtherLibrary.java", 67),
+                new StackTraceElement("SomeClass", "method", "some-file.gradle", 12)
+                ]
+        def usage = Stub(DeprecatedFeatureUsage) {
+            getStack() >> stack
+        }
+        def result = new StringBuilder()
+
+        given:
+        reporter.beforeScript(script)
+        reporter.afterScript(script, null)
+
+        when:
+        reporter.reportLocation(usage, result)
+
+        then:
+        result.length() == 0
+    }
+}
diff --git a/subprojects/core/src/test/groovy/org/gradle/internal/graph/CachingDirectedGraphWalkerTest.groovy b/subprojects/core/src/test/groovy/org/gradle/internal/graph/CachingDirectedGraphWalkerTest.groovy
new file mode 100644
index 0000000..654e59c
--- /dev/null
+++ b/subprojects/core/src/test/groovy/org/gradle/internal/graph/CachingDirectedGraphWalkerTest.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.internal.graph
+
+import spock.lang.Specification
+
+class CachingDirectedGraphWalkerTest extends Specification {
+    private final DirectedGraphWithEdgeValues<Integer, String> graph = Mock()
+    private final CachingDirectedGraphWalker walker = new CachingDirectedGraphWalker(graph)
+
+    def collectsValuesForASingleNode() {
+        when:
+        walker.add(1)
+        def values = walker.findValues()
+
+        then:
+        1 * graph.getNodeValues(1, _, _) >> { args -> args[1] << '1' }
+        0 * _._
+        values == ['1'] as Set
+    }
+
+    def collectsValuesForEachSuccessorNode() {
+        when:
+        walker.add(1)
+        def values = walker.findValues()
+
+        then:
+        1 * graph.getNodeValues(1, _, _) >> { args -> args[1] << '1'; args[2] << 2; args[2] << 3 }
+        1 * graph.getNodeValues(2, _, _) >> { args -> args[1] << '2'; args[2] << 3 }
+        1 * graph.getNodeValues(3, _, _) >> { args -> args[1] << '3' }
+        3 * graph.getEdgeValues(_, _, _)
+        0 * _._
+        values == ['1', '2', '3'] as Set
+    }
+
+    def collectsValuesForEachEdgeTraversed() {
+        when:
+        walker.add(1)
+        def values = walker.findValues()
+
+        then:
+        1 * graph.getNodeValues(1, _, _) >> { args -> args[2] << 2; args[2] << 3 }
+        1 * graph.getEdgeValues(1, 2, _) >> { args -> args[2] << '1->2' }
+        1 * graph.getEdgeValues(1, 3, _) >> { args -> args[2] << '1->3' }
+        1 * graph.getNodeValues(2, _, _) >> { args -> args[2] << 3 }
+        1 * graph.getEdgeValues(2, 3, _) >> { args -> args[2] << '2->3' }
+        1 * graph.getNodeValues(3, _, _)
+        0 * _._
+        values == ['1->2', '1->3', '2->3'] as Set
+    }
+
+    def collectsValuesForAllStartNodes() {
+        when:
+        walker.add(1, 2)
+        def values = walker.findValues()
+
+        then:
+        1 * graph.getNodeValues(1, _, _) >> { args -> args[1] << '1'; args[2] << 3 }
+        1 * graph.getNodeValues(2, _, _) >> { args -> args[1] << '2'; args[2] << 3 }
+        1 * graph.getNodeValues(3, _, _) >> { args -> args[1] << '3' }
+        2 * graph.getEdgeValues(_, _, _)
+        0 * _._
+        values == ['1', '2', '3'] as Set
+    }
+
+    def collectsValuesWhenCycleIsPresentInGraph() {
+        when:
+        walker.add(1)
+        def values = walker.findValues()
+
+        then:
+        1 * graph.getNodeValues(1, _, _) >> { args -> args[1] << '1'; args[2] << 2 }
+        1 * graph.getEdgeValues(1, 2, _) >> { args -> args[2] << '1->2' }
+        1 * graph.getNodeValues(2, _, _) >> { args -> args[1] << '2'; args[2] << 3 }
+        1 * graph.getEdgeValues(2, 3, _) >> { args -> args[2] << '2->3' }
+        1 * graph.getNodeValues(3, _, _) >> { args -> args[1] << '3'; args[2] << 1 }
+        1 * graph.getEdgeValues(3, 1, _) >> { args -> args[2] << '3->1' }
+        0 * _._
+        values == ['1', '1->2', '2', '2->3', '3', '3->1'] as Set
+    }
+
+    def collectsValuesWhenNodeConnectedToItself() {
+        when:
+        walker.add(1)
+        def values = walker.findValues()
+
+        then:
+        1 * graph.getNodeValues(1, _, _) >> { args -> args[1] << '1'; args[2] << 1 }
+        1 * graph.getEdgeValues(1, 1, _) >> { args -> args[2] << '1->1' }
+        0 * _._
+        values == ['1', '1->1'] as Set
+    }
+
+    def collectsValuesWhenMultipleCyclesInGraph() {
+        when:
+        walker.add(1)
+        def values = walker.findValues()
+
+        then:
+        1 * graph.getNodeValues(1, _, _) >> { args -> args[1] << '1'; args[2] << 1; args[2] << 2 }
+        1 * graph.getNodeValues(2, _, _) >> { args -> args[1] << '2'; args[2] << 3; args[2] << 4 }
+        1 * graph.getNodeValues(3, _, _) >> { args -> args[1] << '3'; args[2] << 2 }
+        1 * graph.getNodeValues(4, _, _) >> { args -> args[1] << '4' }
+        5 * graph.getEdgeValues(_, _, _) >> { args -> args[2] << "${args[0]}->${args[1]}".toString() }
+        0 * _._
+        values == ['1', '1->1', '1->2', '2', '2->3', '2->4', '3', '3->2', '4'] as Set
+    }
+
+    def locatesCyclesWhenSingleCycleInGraph() {
+        when:
+        walker.add(1)
+        def values = walker.findCycles()
+
+        then:
+        1 * graph.getNodeValues(1, _, _) >> { args -> args[2] << 2 }
+        1 * graph.getNodeValues(2, _, _) >> { args -> args[2] << 3 }
+        1 * graph.getNodeValues(3, _, _) >> { args -> args[2] << 1; args[2] << 4 }
+        1 * graph.getNodeValues(4, _, _) >> { args -> }
+        _ * graph.getEdgeValues(_, _, _)
+        0 * _._
+
+        and:
+        values == [[1, 2, 3] as Set]
+    }
+
+    def locatesCyclesWhenCycleContainsASingleNode() {
+        when:
+        walker.add(1)
+        def values = walker.findCycles()
+
+        then:
+        1 * graph.getNodeValues(1, _, _) >> { args -> args[2] << 2 }
+        1 * graph.getNodeValues(2, _, _) >> { args -> args[2] << 2 }
+        _ * graph.getEdgeValues(_, _, _)
+        0 * _._
+
+        and:
+        values == [[2] as Set]
+    }
+
+    def locatesCyclesWhenCycleContainsCycles() {
+        when:
+        walker.add(1)
+        def values = walker.findCycles()
+
+        then:
+        1 * graph.getNodeValues(1, _, _) >> { args -> args[2] << 2 }
+        1 * graph.getNodeValues(2, _, _) >> { args -> args[2] << 3 }
+        1 * graph.getNodeValues(3, _, _) >> { args -> args[2] << 4; args[2] << 1 }
+        1 * graph.getNodeValues(4, _, _) >> { args -> args[2] << 3; args[2] << 5 }
+        1 * graph.getNodeValues(5, _, _) >> { args -> args[2] << 4; args[2] << 1 }
+        _ * graph.getEdgeValues(_, _, _)
+        0 * _._
+
+        and:
+        values == [[1, 2, 3, 4, 5] as Set]
+    }
+
+    def locatesCyclesWhenMultipleCyclesInGraph() {
+        when:
+        walker.add(1)
+        def values = walker.findCycles()
+
+        then:
+        1 * graph.getNodeValues(1, _, _) >> { args -> args[2] << 1; args[2] << 2 }
+        1 * graph.getNodeValues(2, _, _) >> { args -> args[2] << 3 }
+        1 * graph.getNodeValues(3, _, _) >> { args -> args[2] << 1; args[2] << 4 }
+        1 * graph.getNodeValues(4, _, _) >> { args -> args[2] << 4; args[2] << 5; }
+        1 * graph.getNodeValues(5, _, _) >> { args -> args[2] << 6 }
+        1 * graph.getNodeValues(6, _, _) >> { args -> args[2] << 5 }
+        _ * graph.getEdgeValues(_, _, _)
+        0 * _._
+
+        and:
+        values == [[5, 6] as Set, [4] as Set, [1, 2, 3] as Set]
+    }
+
+    def canReuseWalkerForMultipleSearches() {
+        when:
+        walker.add(1)
+        def values = walker.findValues()
+
+        then:
+        1 * graph.getNodeValues(1, _, _) >> { args -> args[1] << '1'; args[2] << 2; args[2] << 3 }
+        1 * graph.getNodeValues(2, _, _) >> { args -> args[1] << '2'; args[2] << 3 }
+        1 * graph.getNodeValues(3, _, _) >> { args -> args[1] << '3' }
+        3 * graph.getEdgeValues(_, _, _)
+        0 * _._
+        values == ['1', '2', '3'] as Set
+
+        // Cached node (1) is reachable via 2 separate new paths (4->5->1 and 4->6->1)
+        when:
+        walker.add(4)
+        values = walker.findValues()
+
+        then:
+        1 * graph.getNodeValues(4, _, _) >> { args -> args[1] << '4'; args[2] << 5; args[2] << 6 }
+        1 * graph.getNodeValues(5, _, _) >> { args -> args[1] << '5'; args[2] << 1 }
+        1 * graph.getNodeValues(6, _, _) >> { args -> args[1] << '6'; args[2] << 1 }
+        4 * graph.getEdgeValues(_, _, _) >> { args -> args[2] << "${args[0]}->${args[1]}".toString() }
+        0 * _._
+        values == ['4', '4->5', '4->6', '5', '5->1', '6', '6->1', '1', '2', '3'] as Set
+
+        when:
+        walker.add(2)
+        values = walker.findValues()
+
+        then:
+        values == ['2', '3'] as Set
+    }
+
+    def canReuseWalkerWhenGraphContainsACycle() {
+        when:
+        walker.add(1)
+        walker.findValues()
+
+        then:
+        1 * graph.getNodeValues(1, _, _) >> { args -> args[1] << '1'; args[2] << 2 }
+        1 * graph.getNodeValues(2, _, _) >> { args -> args[1] << '2'; args[2] << 3 }
+        1 * graph.getNodeValues(3, _, _) >> { args -> args[1] << '3'; args[2] << 1; args[2] << 4 }
+        1 * graph.getNodeValues(4, _, _) >> { args -> args[1] << '4'; args[2] << 2 }
+        5 * graph.getEdgeValues(_, _, _)
+        0 * _._
+
+        when:
+        walker.add(1)
+        def values = walker.findValues()
+
+        then:
+        values == ['1', '2', '3', '4'] as Set
+
+        when:
+        walker.add(2)
+        values = walker.findValues()
+
+        then:
+        values == ['1', '2', '3', '4'] as Set
+
+        when:
+        walker.add(3)
+        values = walker.findValues()
+
+        then:
+        values == ['1', '2', '3', '4'] as Set
+
+        when:
+        walker.add(4)
+        values = walker.findValues()
+
+        then:
+        values == ['1', '2', '3', '4'] as Set
+    }
+}
diff --git a/subprojects/core/src/test/groovy/org/gradle/internal/graph/DirectedGraphRendererTest.groovy b/subprojects/core/src/test/groovy/org/gradle/internal/graph/DirectedGraphRendererTest.groovy
new file mode 100644
index 0000000..c11fb3c
--- /dev/null
+++ b/subprojects/core/src/test/groovy/org/gradle/internal/graph/DirectedGraphRendererTest.groovy
@@ -0,0 +1,140 @@
+/*
+ * Copyright 2013 the original author or authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.gradle.internal.graph
+
+import org.gradle.logging.StyledTextOutput
+import org.gradle.logging.TestStyledTextOutput
+import org.gradle.util.TextUtil
+import spock.lang.Specification
+
+class DirectedGraphRendererTest extends Specification {
+    final DirectedGraph<String, Void> graph = Mock(DirectedGraph)
+    final GraphNodeRenderer<String> nodeRenderer = Stub(GraphNodeRenderer) {
+        renderTo(_, _) >> { String node, StyledTextOutput output -> output.text("[$node]")}
+    }
+    final DirectedGraphRenderer<String> renderer = new DirectedGraphRenderer<String>(nodeRenderer, graph)
+    final TestStyledTextOutput output = new TestStyledTextOutput()
+
+    def "renders a graph with a single root node"() {
+        given:
+        1 * graph.getNodeValues("1", _, _) >> { args -> }
+
+        when:
+        renderer.renderTo("1", output)
+
+        then:
+        output.value == """[1]
+"""
+    }
+
+    def "renders a tree"() {
+        given:
+        1 * graph.getNodeValues("1", _, _) >> { args -> args[2] << "2"; args[2] << "3" }
+        1 * graph.getNodeValues("2", _, _) >> { args -> args[2] << "4" }
+        1 * graph.getNodeValues("3", _, _) >> { args -> }
+        1 * graph.getNodeValues("4", _, _) >> { args -> args[2] << "5" }
+        1 * graph.getNodeValues("5", _, _) >> { args -> }
+
+        when:
+        renderer.renderTo("1", output)
+
+        then:
+        output.value == """[1]
+{info}+--- {normal}[2]
+{info}|    \\--- {normal}[4]
+{info}|         \\--- {normal}[5]
+{info}\\--- {normal}[3]
+"""
+    }
+
+    def "renders a graph that contains nodes with multiple incoming edges"() {
+        given:
+        1 * graph.getNodeValues("1", _, _) >> { args -> args[2] << "2"; args[2] << "3" }
+        1 * graph.getNodeValues("2", _, _) >> { args -> args[2] << "4" }
+        1 * graph.getNodeValues("3", _, _) >> { args -> args[2] << "2" }
+        1 * graph.getNodeValues("4", _, _) >> { args -> }
+
+        when:
+        renderer.renderTo("1", output)
+
+        then:
+        output.value == """[1]
+{info}+--- {normal}[2]
+{info}|    \\--- {normal}[4]
+{info}\\--- {normal}[3]
+{info}     \\--- {normal}[2] (*)
+
+{info}(*) - details omitted (listed previously){normal}
+"""
+    }
+
+    def "renders a graph that contains cycles"() {
+        given:
+        1 * graph.getNodeValues("1", _, _) >> { args -> args[2] << "2"; args[2] << "3" }
+        1 * graph.getNodeValues("2", _, _) >> { args -> args[2] << "3" }
+        1 * graph.getNodeValues("3", _, _) >> { args -> args[2] << "2" }
+
+        when:
+        renderer.renderTo("1", output)
+
+        then:
+        output.value == """[1]
+{info}+--- {normal}[2]
+{info}|    \\--- {normal}[3]
+{info}|         \\--- {normal}[2] (*)
+{info}\\--- {normal}[3] (*)
+
+{info}(*) - details omitted (listed previously){normal}
+"""
+    }
+
+    def "renders a graph that contains a single node with an edge to itself"() {
+        given:
+        1 * graph.getNodeValues("1", _, _) >> { args -> args[2] << "1" }
+
+        when:
+        renderer.renderTo("1", output)
+
+        then:
+        output.value == """[1]
+{info}\\--- {normal}[1] (*)
+
+{info}(*) - details omitted (listed previously){normal}
+"""
+    }
+
+    def "renders to an appendable"() {
+        given:
+        1 * graph.getNodeValues("1", _, _) >> { args -> args[2] << "2"; args[2] << "3" }
+        1 * graph.getNodeValues("2", _, _) >> { args -> args[2] << "4" }
+        1 * graph.getNodeValues("3", _, _) >> { args -> }
+        1 * graph.getNodeValues("4", _, _) >> { args -> args[2] << "5" }
+        1 * graph.getNodeValues("5", _, _) >> { args -> }
+
+        when:
+        StringWriter writer = new StringWriter()
+        renderer.renderTo("1", writer)
+
+        then:
+        writer.toString() == TextUtil.toPlatformLineSeparators("""[1]
++--- [2]
+|    \\--- [4]
+|         \\--- [5]
+\\--- [3]
+""")
+    }
+
+}
diff --git a/subprojects/core/src/test/groovy/org/gradle/internal/graph/GraphAggregatorTest.groovy b/subprojects/core/src/test/groovy/org/gradle/internal/graph/GraphAggregatorTest.groovy
new file mode 100644
index 0000000..9faebe9
--- /dev/null
+++ b/subprojects/core/src/test/groovy/org/gradle/internal/graph/GraphAggregatorTest.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.internal.graph
+
+import spock.lang.Specification
+
+class GraphAggregatorTest extends Specification {
+    private final DirectedGraph<String, String> graph = Mock()
+    private final GraphAggregator aggregator = new GraphAggregator(graph)
+
+    def groupsNodeWithTheEntryNodeItIsReachableFrom() {
+        when:
+        def result = aggregator.group(['a'], ['a', 'b'])
+
+        then:
+        1 * graph.getNodeValues('a', !null, !null) >> { args -> args[2].add('b') }
+        result.getNodes('a') == ['a', 'b'] as Set
+    }
+
+    def groupsNodeWithTheClosesEntryNodeItIsReachableFrom() {
+        when:
+        def result = aggregator.group(['a', 'b'], ['a', 'b', 'c'])
+
+        then:
+        1 * graph.getNodeValues('a', !null, !null) >> { args -> args[2].add('b') }
+        1 * graph.getNodeValues('b', !null, !null) >> { args -> args[2].add('c') }
+        result.getNodes('a') == ['a'] as Set
+        result.getNodes('b') == ['b', 'c'] as Set
+    }
+
+    def groupsNodeWithMultipleEntryNodesWhenTheNodeHasMultipleClosesNodes() {
+        when:
+        def result = aggregator.group(['a', 'b'], ['a', 'b', 'c'])
+
+        then:
+        1 * graph.getNodeValues('a', !null, !null) >> { args -> args[2].add('c') }
+        1 * graph.getNodeValues('b', !null, !null) >> { args -> args[2].add('c') }
+        result.getNodes('a') == ['a', 'c'] as Set
+        result.getNodes('b') == ['b', 'c'] as Set
+    }
+
+    def groupsNodesWhichAreNotReachableFromStartNodes() {
+        when:
+        def result = aggregator.group(['a', 'b'], ['a', 'b', 'c', 'd'])
+
+        then:
+        1 * graph.getNodeValues('a', !null, !null) >> { args -> args[2].add('b') }
+        1 * graph.getNodeValues('c', !null, !null) >> { args -> args[2].add('d') }
+        result.topLevelNodes == ['a', 'b', 'c'] as Set
+        result.getNodes('c') == ['c', 'd'] as Set
+    }
+}
diff --git a/subprojects/core/src/test/groovy/org/gradle/internal/service/scopes/BuildScopeServicesTest.groovy b/subprojects/core/src/test/groovy/org/gradle/internal/service/scopes/BuildScopeServicesTest.groovy
new file mode 100644
index 0000000..73b0c0b
--- /dev/null
+++ b/subprojects/core/src/test/groovy/org/gradle/internal/service/scopes/BuildScopeServicesTest.groovy
@@ -0,0 +1,270 @@
+/*
+ * Copyright 2010 the original author or authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.gradle.internal.service.scopes
+
+import org.gradle.StartParameter
+import org.gradle.api.internal.*
+import org.gradle.api.internal.classpath.DefaultModuleRegistry
+import org.gradle.api.internal.classpath.ModuleRegistry
+import org.gradle.api.internal.classpath.PluginModuleRegistry
+import org.gradle.api.internal.file.FileResolver
+import org.gradle.api.internal.project.DefaultIsolatedAntBuilder
+import org.gradle.api.internal.project.IProjectFactory
+import org.gradle.api.internal.project.IsolatedAntBuilder
+import org.gradle.api.internal.project.ProjectFactory
+import org.gradle.cache.CacheRepository
+import org.gradle.cache.internal.CacheFactory
+import org.gradle.cache.internal.DefaultCacheRepository
+import org.gradle.configuration.BuildConfigurer
+import org.gradle.configuration.DefaultBuildConfigurer
+import org.gradle.configuration.DefaultScriptPluginFactory
+import org.gradle.configuration.ScriptPluginFactory
+import org.gradle.groovy.scripts.DefaultScriptCompilerFactory
+import org.gradle.groovy.scripts.ScriptCompilerFactory
+import org.gradle.initialization.*
+import org.gradle.internal.Factory
+import org.gradle.internal.reflect.Instantiator
+import org.gradle.internal.service.ServiceRegistry
+import org.gradle.invocation.BuildClassLoaderRegistry
+import org.gradle.invocation.DefaultBuildClassLoaderRegistry
+import org.gradle.listener.DefaultListenerManager
+import org.gradle.listener.ListenerManager
+import org.gradle.logging.LoggingManagerInternal
+import org.gradle.messaging.remote.MessagingServer
+import org.gradle.process.internal.DefaultWorkerProcessFactory
+import org.gradle.process.internal.WorkerProcessBuilder
+import org.gradle.profile.ProfileEventAdapter
+import org.gradle.test.fixtures.file.TestNameTestDirectoryProvider
+import org.gradle.internal.classloader.ClassLoaderFactory
+import org.gradle.internal.classloader.MultiParentClassLoader
+import org.junit.Rule
+import spock.lang.Specification
+import spock.lang.Timeout
+
+import static org.hamcrest.Matchers.instanceOf
+import static org.hamcrest.Matchers.sameInstance
+import static org.junit.Assert.assertThat
+
+public class BuildScopeServicesTest extends Specification {
+    @Rule
+    TestNameTestDirectoryProvider tmpDir = new TestNameTestDirectoryProvider()
+    StartParameter startParameter = new StartParameter()
+    ServiceRegistry parent = Mock()
+    Factory<CacheFactory> cacheFactoryFactory = Mock()
+    ClosableCacheFactory cacheFactory = Mock()
+    ClassLoaderRegistry classLoaderRegistry = Mock()
+
+    BuildScopeServices registry = new BuildScopeServices(parent, startParameter)
+
+    def setup() {
+        startParameter.gradleUserHomeDir = tmpDir.testDirectory
+        parent.getFactory(CacheFactory) >> cacheFactoryFactory
+        cacheFactoryFactory.create() >> cacheFactory
+        parent.get(ClassLoaderRegistry) >> classLoaderRegistry
+        parent.getFactory(LoggingManagerInternal) >> Stub(Factory)
+        parent.get(ModuleRegistry) >> new DefaultModuleRegistry()
+        parent.get(PluginModuleRegistry) >> Stub(PluginModuleRegistry)
+        parent.get(Instantiator) >> ThreadGlobalInstantiator.getOrCreate()
+        parent.get(FileResolver) >> Stub(FileResolver)
+    }
+
+    def delegatesToParentForUnknownService() {
+        setup:
+        parent.get(String) >> "value"
+
+        expect:
+        registry.get(String) == "value"
+    }
+
+    def throwsExceptionForUnknownDomainObject() {
+        when:
+        registry.createFor("string")
+        then:
+        def e = thrown(IllegalArgumentException)
+        e.message == "Cannot create services for unknown domain object of type String."
+    }
+
+    def canCreateServicesForAGradleInstance() {
+        setup:
+        GradleInternal gradle = Mock()
+        ServiceRegistryFactory registry = this.registry.createFor(gradle)
+        expect:
+        registry instanceof GradleScopeServices
+    }
+
+    def providesAListenerManager() {
+        setup:
+        ListenerManager listenerManager = expectListenerManagerCreated()
+        expect:
+        assertThat(registry.get(ListenerManager), sameInstance(listenerManager))
+    }
+
+    @Timeout(5)
+    def providesAScriptCompilerFactory() {
+        setup:
+        expectListenerManagerCreated()
+
+        expect:
+        registry.get(ScriptCompilerFactory) instanceof DefaultScriptCompilerFactory
+        registry.get(ScriptCompilerFactory) == registry.get(ScriptCompilerFactory)
+    }
+
+    def providesACacheRepositoryAndCleansUpOnClose() {
+        setup:
+        1 * cacheFactory.close()
+
+        expect:
+        registry.get(CacheRepository) instanceof DefaultCacheRepository
+        registry.get(CacheRepository) == registry.get(CacheRepository)
+        registry.close()
+    }
+
+    def providesAnInitScriptHandler() {
+        setup:
+        allowGetCoreImplClassLoader()
+        expectScriptClassLoaderCreated()
+        expectListenerManagerCreated()
+        allowGetGradleDistributionLocator()
+
+        expect:
+        registry.get(InitScriptHandler) instanceof InitScriptHandler
+        registry.get(InitScriptHandler) == registry.get(InitScriptHandler)
+    }
+
+    def providesAScriptObjectConfigurerFactory() {
+        setup:
+        allowGetCoreImplClassLoader()
+        expectListenerManagerCreated()
+        expectScriptClassLoaderCreated()
+        expect:
+        assertThat(registry.get(ScriptPluginFactory), instanceOf(DefaultScriptPluginFactory))
+        assertThat(registry.get(ScriptPluginFactory), sameInstance(registry.get(ScriptPluginFactory)))
+    }
+
+    def providesASettingsProcessor() {
+        setup:
+        allowGetCoreImplClassLoader()
+        expectListenerManagerCreated()
+        expectScriptClassLoaderCreated()
+        expect:
+        assertThat(registry.get(SettingsProcessor), instanceOf(PropertiesLoadingSettingsProcessor))
+        assertThat(registry.get(SettingsProcessor), sameInstance(registry.get(SettingsProcessor)))
+    }
+
+    def providesAnExceptionAnalyser() {
+        setup:
+        expectListenerManagerCreated()
+        expect:
+        assertThat(registry.get(ExceptionAnalyser), instanceOf(MultipleBuildFailuresExceptionAnalyser))
+        assertThat(registry.get(ExceptionAnalyser).delegate, instanceOf(DefaultExceptionAnalyser))
+        assertThat(registry.get(ExceptionAnalyser), sameInstance(registry.get(ExceptionAnalyser)))
+    }
+
+    def providesAWorkerProcessFactory() {
+        setup:
+        expectParentServiceLocated(MessagingServer)
+        allowGetCoreImplClassLoader()
+
+        expect:
+        assertThat(registry.getFactory(WorkerProcessBuilder), instanceOf(DefaultWorkerProcessFactory))
+    }
+
+    def providesAnIsolatedAntBuilder() {
+        setup:
+        expectParentServiceLocated(ClassLoaderFactory)
+        allowGetCoreImplClassLoader()
+        expect:
+
+        assertThat(registry.get(IsolatedAntBuilder), instanceOf(DefaultIsolatedAntBuilder))
+        assertThat(registry.get(IsolatedAntBuilder), sameInstance(registry.get(IsolatedAntBuilder)))
+    }
+
+    def providesAProjectFactory() {
+        setup:
+        expectParentServiceLocated(Instantiator)
+        expectParentServiceLocated(ClassGenerator)
+        expect:
+        assertThat(registry.get(IProjectFactory), instanceOf(ProjectFactory))
+        assertThat(registry.get(IProjectFactory), sameInstance(registry.get(IProjectFactory)))
+    }
+
+    def providesABuildConfigurer() {
+        expect:
+        assertThat(registry.get(BuildConfigurer), instanceOf(DefaultBuildConfigurer))
+        assertThat(registry.get(BuildConfigurer), sameInstance(registry.get(BuildConfigurer)))
+    }
+
+    def providesAPropertiesLoader() {
+        expect:
+        assertThat(registry.get(IGradlePropertiesLoader), instanceOf(DefaultGradlePropertiesLoader))
+        assertThat(registry.get(IGradlePropertiesLoader), sameInstance(registry.get(IGradlePropertiesLoader)))
+    }
+
+    def providesABuildLoader() {
+        setup:
+        expectParentServiceLocated(Instantiator)
+        expect:
+        assertThat(registry.get(BuildLoader), instanceOf(ProjectPropertySettingBuildLoader))
+        assertThat(registry.get(BuildLoader), sameInstance(registry.get(BuildLoader)))
+    }
+
+    def providesAProfileEventAdapter() {
+        setup:
+        expectParentServiceLocated(BuildRequestMetaData)
+        expectListenerManagerCreated()
+
+        expect:
+        assertThat(registry.get(ProfileEventAdapter), instanceOf(ProfileEventAdapter))
+        assertThat(registry.get(ProfileEventAdapter), sameInstance(registry.get(ProfileEventAdapter)))
+    }
+
+    def providesABuildClassLoaderRegistry() {
+        expect:
+        assertThat(registry.get(BuildClassLoaderRegistry), instanceOf(DefaultBuildClassLoaderRegistry))
+        assertThat(registry.get(BuildClassLoaderRegistry), sameInstance(registry.get(BuildClassLoaderRegistry)))
+    }
+
+    private <T> T expectParentServiceLocated(Class<T> type) {
+        T t = Mock(type)
+        parent.get(type) >> t
+        t
+    }
+
+    private ListenerManager expectListenerManagerCreated() {
+        final ListenerManager listenerManager = new DefaultListenerManager()
+        final ListenerManager listenerManagerParent = Mock()
+        parent.get(ListenerManager) >> listenerManagerParent
+        1 * listenerManagerParent.createChild() >> listenerManager
+        listenerManager
+    }
+
+    private void allowGetCoreImplClassLoader() {
+        classLoaderRegistry.getCoreImplClassLoader() >> new ClassLoader() {}
+    }
+
+    private void expectScriptClassLoaderCreated() {
+        1 * classLoaderRegistry.gradleApiClassLoader >> new MultiParentClassLoader()
+    }
+
+    private void allowGetGradleDistributionLocator() {
+        parent.get(GradleDistributionLocator) >> Mock(GradleDistributionLocator)
+    }
+
+    public interface ClosableCacheFactory extends CacheFactory {
+        void close()
+    }
+}
\ No newline at end of file
diff --git a/subprojects/core/src/test/groovy/org/gradle/internal/service/scopes/GlobalScopeServicesTest.java b/subprojects/core/src/test/groovy/org/gradle/internal/service/scopes/GlobalScopeServicesTest.java
new file mode 100755
index 0000000..54b0309
--- /dev/null
+++ b/subprojects/core/src/test/groovy/org/gradle/internal/service/scopes/GlobalScopeServicesTest.java
@@ -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.internal.service.scopes;
+
+import org.gradle.api.internal.*;
+import org.gradle.api.internal.classpath.DefaultModuleRegistry;
+import org.gradle.api.internal.classpath.DefaultPluginModuleRegistry;
+import org.gradle.api.internal.classpath.ModuleRegistry;
+import org.gradle.api.internal.classpath.PluginModuleRegistry;
+import org.gradle.api.internal.file.FileResolver;
+import org.gradle.api.internal.file.IdentityFileResolver;
+import org.gradle.cache.internal.CacheFactory;
+import org.gradle.cache.internal.DefaultCacheFactory;
+import org.gradle.cache.internal.DefaultFileLockManager;
+import org.gradle.cache.internal.FileLockManager;
+import org.gradle.cli.CommandLineConverter;
+import org.gradle.initialization.*;
+import org.gradle.internal.concurrent.DefaultExecutorFactory;
+import org.gradle.internal.concurrent.ExecutorFactory;
+import org.gradle.internal.nativeplatform.ProcessEnvironment;
+import org.gradle.internal.nativeplatform.filesystem.FileSystem;
+import org.gradle.listener.DefaultListenerManager;
+import org.gradle.listener.ListenerManager;
+import org.gradle.logging.LoggingManagerInternal;
+import org.gradle.logging.ProgressLoggerFactory;
+import org.gradle.logging.internal.DefaultLoggingManagerFactory;
+import org.gradle.logging.internal.DefaultProgressLoggerFactory;
+import org.gradle.messaging.remote.MessagingServer;
+import org.gradle.internal.classloader.ClassLoaderFactory;
+import org.gradle.internal.classloader.DefaultClassLoaderFactory;
+import org.junit.Test;
+import spock.lang.Shared;
+
+import static org.hamcrest.Matchers.instanceOf;
+import static org.hamcrest.Matchers.notNullValue;
+import static org.junit.Assert.assertThat;
+
+public class GlobalScopeServicesTest {
+    @Shared
+    private final GlobalScopeServices registry = new GlobalScopeServices();
+
+    @Test
+    public void providesAGradleLauncherFactory() {
+        assertThat(registry.get(GradleLauncherFactory.class), instanceOf(DefaultGradleLauncherFactory.class));
+    }
+
+    @Test
+    public void providesCommandLineArgsConverter() {
+        assertThat(registry.get(CommandLineConverter.class), instanceOf(
+                DefaultCommandLineConverter.class));
+    }
+
+    @Test
+    public void providesACacheFactoryFactory() {
+        assertThat(registry.getFactory(CacheFactory.class), instanceOf(DefaultCacheFactory.class));
+    }
+
+    @Test
+    public void providesAModuleRegistry() {
+        assertThat(registry.get(ModuleRegistry.class), instanceOf(DefaultModuleRegistry.class));
+    }
+
+    @Test
+    public void providesAPluginModuleRegistry() {
+        assertThat(registry.get(PluginModuleRegistry.class), instanceOf(DefaultPluginModuleRegistry.class));
+    }
+
+    @Test
+    public void providesAClassPathRegistry() {
+        assertThat(registry.get(ClassPathRegistry.class), instanceOf(DefaultClassPathRegistry.class));
+    }
+
+    @Test
+    public void providesAClassLoaderRegistry() {
+        assertThat(registry.get(ClassLoaderRegistry.class), instanceOf(DefaultClassLoaderRegistry.class));
+    }
+
+    @Test
+    public void providesALoggingManagerFactory() {
+        assertThat(registry.getFactory(LoggingManagerInternal.class), instanceOf(DefaultLoggingManagerFactory.class));
+    }
+    
+    @Test
+    public void providesAListenerManager() {
+        assertThat(registry.get(ListenerManager.class), instanceOf(DefaultListenerManager.class));
+    }
+    
+    @Test
+    public void providesAProgressLoggerFactory() {
+        assertThat(registry.get(ProgressLoggerFactory.class), instanceOf(DefaultProgressLoggerFactory.class));
+    }
+    
+    @Test
+    public void providesAGradleDistributionLocator() {
+        assertThat(registry.get(GradleDistributionLocator.class), instanceOf(DefaultModuleRegistry.class));
+    }
+    
+    @Test
+    public void providesAClassLoaderFactory() {
+        assertThat(registry.get(ClassLoaderFactory.class), instanceOf(DefaultClassLoaderFactory.class));
+    }
+
+    @Test
+    public void providesAMessagingServer() {
+        assertThat(registry.get(MessagingServer.class), instanceOf(MessagingServer.class));
+    }
+
+    @Test
+    public void providesAClassGenerator() {
+        assertThat(registry.get(ClassGenerator.class), instanceOf(AsmBackedClassGenerator.class));
+    }
+
+    @Test
+    public void providesAnInstantiator() {
+        assertThat(registry.get(org.gradle.internal.reflect.Instantiator.class), instanceOf(ClassGeneratorBackedInstantiator.class));
+    }
+
+    @Test
+    public void providesAnExecutorFactory() {
+        assertThat(registry.get(ExecutorFactory.class), instanceOf(DefaultExecutorFactory.class));
+    }
+
+    @Test
+    public void providesAFileLockManager() {
+        assertThat(registry.get(FileLockManager.class), instanceOf(DefaultFileLockManager.class));
+    }
+
+    @Test
+    public void providesAProcessEnvironment() {
+        assertThat(registry.get(ProcessEnvironment.class), notNullValue());
+    }
+
+    @Test
+    public void providesAFileSystem() {
+        assertThat(registry.get(FileSystem.class), notNullValue());
+    }
+
+    @Test
+    public void providesAFileResolver() {
+        assertThat(registry.get(FileResolver.class), instanceOf(IdentityFileResolver.class));
+    }
+
+    @Test
+    public void providesADocumentationRegistry() throws Exception {
+        assertThat(registry.get(DocumentationRegistry.class), instanceOf(DocumentationRegistry.class));
+    }
+}
diff --git a/subprojects/core/src/test/groovy/org/gradle/internal/service/scopes/GradleScopeServicesTest.groovy b/subprojects/core/src/test/groovy/org/gradle/internal/service/scopes/GradleScopeServicesTest.groovy
new file mode 100644
index 0000000..c91a3bb
--- /dev/null
+++ b/subprojects/core/src/test/groovy/org/gradle/internal/service/scopes/GradleScopeServicesTest.groovy
@@ -0,0 +1,119 @@
+/*
+ * 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.internal.service.scopes
+
+import org.gradle.StartParameter
+import org.gradle.api.internal.GradleInternal
+import org.gradle.api.internal.plugins.DefaultPluginContainer
+import org.gradle.api.internal.plugins.PluginRegistry
+import org.gradle.api.internal.project.DefaultProjectRegistry
+import org.gradle.api.internal.project.ProjectInternal
+import org.gradle.api.internal.project.ProjectRegistry
+import org.gradle.api.plugins.PluginContainer
+import org.gradle.cache.CacheRepository
+import org.gradle.execution.BuildExecuter
+import org.gradle.execution.DefaultBuildExecuter
+import org.gradle.execution.TaskGraphExecuter
+import org.gradle.execution.taskgraph.DefaultTaskGraphExecuter
+import org.gradle.internal.classloader.MultiParentClassLoader
+import org.gradle.internal.service.ServiceRegistry
+import org.gradle.invocation.BuildClassLoaderRegistry
+import org.gradle.listener.ListenerManager
+import spock.lang.Specification
+
+import static org.hamcrest.Matchers.sameInstance
+
+public class GradleScopeServicesTest extends Specification {
+    private GradleInternal gradle = Mock()
+    private ServiceRegistry parent = Mock()
+    private ListenerManager listenerManager = Mock()
+    private CacheRepository cacheRepository = Mock()
+    private GradleScopeServices registry = new GradleScopeServices(parent, gradle)
+    private StartParameter startParameter = new StartParameter()
+    private PluginRegistry pluginRegistryParent = Mock()
+    private PluginRegistry pluginRegistryChild = Mock()
+
+    public void setup() {
+        parent.get(StartParameter) >> Mock(StartParameter)
+        parent.get(ListenerManager) >> listenerManager
+        parent.get(CacheRepository) >> cacheRepository
+        parent.get(PluginRegistry) >> pluginRegistryParent
+        parent.get(BuildClassLoaderRegistry) >> Stub(BuildClassLoaderRegistry)
+        gradle.getStartParameter() >> startParameter
+        gradle.getScriptClassLoader() >> new MultiParentClassLoader()
+        pluginRegistryParent.createChild(_, _) >> pluginRegistryChild
+    }
+
+    def "can create services for a project instance"() {
+        ProjectInternal project = Mock()
+
+        when:
+        ServiceRegistryFactory serviceRegistry = registry.createFor(project)
+
+        then:
+        serviceRegistry instanceof ProjectScopeServices
+    }
+
+    def "provides a project registry"() {
+        when:
+        def projectRegistry = registry.get(ProjectRegistry)
+        def secondRegistry = registry.get(ProjectRegistry)
+
+        then:
+        projectRegistry instanceof DefaultProjectRegistry
+        projectRegistry sameInstance(secondRegistry)
+    }
+
+    def "provides a plugin registry"() {
+        when:
+        def pluginRegistry = registry.get(PluginRegistry)
+        def secondRegistry = registry.get(PluginRegistry)
+
+        then:
+        pluginRegistry == pluginRegistryChild
+        secondRegistry sameInstance(pluginRegistry)
+    }
+
+    def "provides a build executer"() {
+        when:
+        def buildExecuter = registry.get(BuildExecuter)
+        def secondExecuter = registry.get(BuildExecuter)
+
+        then:
+        buildExecuter instanceof DefaultBuildExecuter
+        buildExecuter sameInstance(secondExecuter)
+    }
+
+    def "provides a plugin container"() {
+        when:
+        def pluginContainer = registry.get(PluginContainer)
+        def secondPluginContainer = registry.get(PluginContainer)
+
+        then:
+        pluginContainer instanceof DefaultPluginContainer
+        secondPluginContainer sameInstance(pluginContainer)
+    }
+
+    def "provides a task graph executer"() {
+        when:
+        def graphExecuter = registry.get(TaskGraphExecuter)
+        def secondExecuter = registry.get(TaskGraphExecuter)
+
+        then:
+        graphExecuter instanceof DefaultTaskGraphExecuter
+        graphExecuter sameInstance(secondExecuter)
+    }
+}
diff --git a/subprojects/core/src/test/groovy/org/gradle/internal/service/scopes/ProjectScopeServicesTest.groovy b/subprojects/core/src/test/groovy/org/gradle/internal/service/scopes/ProjectScopeServicesTest.groovy
new file mode 100644
index 0000000..3b5cffa
--- /dev/null
+++ b/subprojects/core/src/test/groovy/org/gradle/internal/service/scopes/ProjectScopeServicesTest.groovy
@@ -0,0 +1,222 @@
+/*
+ * Copyright 2010 the original author or authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.gradle.internal.service.scopes
+
+import org.gradle.api.AntBuilder
+import org.gradle.api.artifacts.dsl.ArtifactHandler
+import org.gradle.api.artifacts.dsl.DependencyHandler
+import org.gradle.api.artifacts.dsl.RepositoryHandler
+import org.gradle.api.initialization.dsl.ScriptHandler
+import org.gradle.api.internal.*
+import org.gradle.api.internal.artifacts.ArtifactPublicationServices
+import org.gradle.api.internal.artifacts.DependencyManagementServices
+import org.gradle.api.internal.artifacts.DependencyResolutionServices
+import org.gradle.api.internal.artifacts.configurations.ConfigurationContainerInternal
+import org.gradle.api.internal.artifacts.dsl.dependencies.DependencyFactory
+import org.gradle.api.internal.file.*
+import org.gradle.api.internal.initialization.DefaultScriptHandler
+import org.gradle.api.internal.initialization.ScriptClassLoaderProvider
+import org.gradle.api.internal.plugins.DefaultPluginContainer
+import org.gradle.api.internal.plugins.PluginRegistry
+import org.gradle.api.internal.project.DefaultAntBuilderFactory
+import org.gradle.api.internal.project.ProjectInternal
+import org.gradle.api.internal.project.taskfactory.ITaskFactory
+import org.gradle.api.internal.tasks.DefaultTaskContainerFactory
+import org.gradle.api.internal.tasks.TaskContainerInternal
+import org.gradle.api.logging.LoggingManager
+import org.gradle.api.plugins.PluginContainer
+import org.gradle.configuration.project.DefaultProjectConfigurationActionContainer
+import org.gradle.configuration.project.ProjectConfigurationActionContainer
+import org.gradle.groovy.scripts.ScriptSource
+import org.gradle.initialization.ProjectAccessListener
+import org.gradle.internal.Factory
+import org.gradle.internal.reflect.DirectInstantiator
+import org.gradle.internal.reflect.Instantiator
+import org.gradle.internal.service.ServiceRegistry
+import org.gradle.internal.nativeplatform.filesystem.FileSystem
+import org.gradle.invocation.BuildClassLoaderRegistry
+import org.gradle.logging.LoggingManagerInternal
+import org.gradle.tooling.provider.model.ToolingModelBuilderRegistry
+import org.gradle.tooling.provider.model.internal.DefaultToolingModelBuilderRegistry
+
+import spock.lang.Specification
+
+class ProjectScopeServicesTest extends Specification {
+    ProjectInternal project = Mock()
+    ConfigurationContainerInternal configurationContainer = Mock()
+    GradleInternal gradle = Mock()
+    DependencyManagementServices dependencyManagementServices = Mock()
+    ITaskFactory taskFactory = Mock()
+    DependencyFactory dependencyFactory = Mock()
+    ServiceRegistry parent = Stub()
+    ProjectScopeServices registry = new ProjectScopeServices(parent, project)
+    PluginRegistry pluginRegistry = Mock()
+    DependencyResolutionServices dependencyResolutionServices = Stub()
+    RepositoryHandler repositoryHandler = Mock()
+    ArtifactPublicationServices publicationServices = Mock()
+    DependencyHandler dependencyHandler = Mock()
+    ArtifactHandler artifactHandler = Mock()
+
+    def setup() {
+        project.gradle >> gradle
+        project.projectDir >> new File("project-dir").absoluteFile
+        project.buildScriptSource >> Stub(ScriptSource)
+        parent.get(ITaskFactory) >> taskFactory
+        parent.get(DependencyFactory) >> dependencyFactory
+        parent.get(PluginRegistry) >> pluginRegistry
+        parent.get(DependencyManagementServices) >> dependencyManagementServices
+        parent.get(Instantiator) >> new DirectInstantiator()
+        parent.get(FileSystem) >> Stub(FileSystem)
+        parent.get(ClassGenerator) >> Stub(ClassGenerator)
+        parent.get(ProjectAccessListener) >> Stub(ProjectAccessListener)
+        parent.get(BuildClassLoaderRegistry) >> Stub(BuildClassLoaderRegistry) {
+            getScriptClassLoader() >> new ClassLoader() { }
+        }
+    }
+
+    def "creates a registry for a task"() {
+        expect:
+        registry.createFor(Stub(TaskInternal)) instanceof TaskScopeServices
+    }
+
+    def "provides a TaskContainerFactory"() {
+        1 * taskFactory.createChild({ it.is project }, { it instanceof ClassGeneratorBackedInstantiator }) >> Stub(ITaskFactory)
+
+        expect:
+        registry.getFactory(TaskContainerInternal) instanceof DefaultTaskContainerFactory
+    }
+
+    def "provides a PluginContainer"() {
+        expectScriptClassLoaderProviderCreated()
+
+        1 * pluginRegistry.createChild(!null, _ as DependencyInjectingInstantiator) >> Stub(PluginRegistry)
+
+        expect:
+        provides(PluginContainer, DefaultPluginContainer)
+    }
+
+    def "provides a ToolingModelBuilderRegistry"() {
+        expect:
+        provides(ToolingModelBuilderRegistry, DefaultToolingModelBuilderRegistry)
+    }
+
+    def "provides an ArtifactPublicationServices factory"() {
+        expectDependencyResolutionServicesCreated()
+
+        expect:
+        registry.get(ArtifactPublicationServices).is publicationServices
+    }
+
+    def "provides a RepositoryHandler"() {
+        expectDependencyResolutionServicesCreated()
+
+        expect:
+        registry.get(RepositoryHandler).is repositoryHandler
+        registry.get(RepositoryHandler).is registry.get(RepositoryHandler)
+    }
+
+    def "provides a ConfigurationContainer"() {
+        expectDependencyResolutionServicesCreated()
+
+        expect:
+        registry.get(ConfigurationContainerInternal).is configurationContainer
+        registry.get(ConfigurationContainerInternal).is registry.get(ConfigurationContainerInternal)
+    }
+
+    def "provides an ArtifactHandler"() {
+        expectDependencyResolutionServicesCreated()
+
+        registry.get(ArtifactHandler).is artifactHandler
+        registry.get(ArtifactHandler).is registry.get(ArtifactHandler)
+    }
+
+    def "provides a DependencyHandler"() {
+        expectDependencyResolutionServicesCreated()
+
+        expect:
+        registry.get(DependencyHandler).is dependencyHandler
+        registry.get(DependencyHandler).is registry.get(DependencyHandler)
+    }
+
+    def "provides an AntBuilder factory"() {
+        expect:
+        registry.getFactory(AntBuilder) instanceof DefaultAntBuilderFactory
+        registry.getFactory(AntBuilder).is registry.getFactory(AntBuilder)
+    }
+
+    def "provides a ScriptHandler and ScriptClassLoaderProvider"() {
+        expectScriptClassLoaderProviderCreated()
+
+        expect:
+        provides(ScriptHandler, DefaultScriptHandler)
+        registry.get(ScriptClassLoaderProvider).is registry.get(ScriptHandler)
+    }
+
+    def "provides a FileResolver"() {
+        expect:
+        provides(FileResolver, BaseDirFileResolver)
+    }
+
+    def "provides a FileOperations instance"() {
+        1 * project.tasks
+
+        expect:
+        provides(FileOperations, DefaultFileOperations)
+    }
+    
+    def "provides a TemporaryFileProvider"() {
+        expect:
+        provides(TemporaryFileProvider, DefaultTemporaryFileProvider)
+    }
+
+    def "provides a ProjectConfigurationActionContainer"() {
+        expect:
+        provides(ProjectConfigurationActionContainer, DefaultProjectConfigurationActionContainer)
+    }
+
+    def "provides a LoggingManager"() {
+        Factory<LoggingManagerInternal> loggingManagerFactory = Mock()
+        LoggingManager loggingManager = Mock(LoggingManagerInternal)
+
+        parent.getFactory(LoggingManagerInternal) >> loggingManagerFactory
+        1 * loggingManagerFactory.create() >> loggingManager
+
+        expect:
+        registry.get(LoggingManager).is loggingManager
+        registry.get(LoggingManager).is registry.get(LoggingManager)
+    }
+
+    void provides(Class<?> contractType, Class<?> implementationType) {
+        assert implementationType.isInstance(registry.get(contractType))
+        assert registry.get(contractType).is(registry.get(contractType))
+    }
+
+    private void expectScriptClassLoaderProviderCreated() {
+        1 * dependencyManagementServices.create(!null, !null, !null, !null) >> dependencyResolutionServices
+        // return mock rather than stub; workaround for fact that Spock doesn't substitute generic method return type as it should
+        dependencyResolutionServices.configurationContainer >> Mock(ConfigurationContainerInternal)
+    }
+
+    private void expectDependencyResolutionServicesCreated() {
+        1 * dependencyManagementServices.create(!null, !null, !null, !null) >> dependencyResolutionServices
+        dependencyResolutionServices.resolveRepositoryHandler >> repositoryHandler
+        dependencyResolutionServices.createArtifactPublicationServices() >> publicationServices
+        dependencyResolutionServices.configurationContainer >> configurationContainer
+        dependencyResolutionServices.dependencyHandler >> dependencyHandler
+        dependencyResolutionServices.artifactHandler >> artifactHandler
+    }
+}
diff --git a/subprojects/core/src/test/groovy/org/gradle/internal/service/scopes/SettingsScopeServicesTest.groovy b/subprojects/core/src/test/groovy/org/gradle/internal/service/scopes/SettingsScopeServicesTest.groovy
new file mode 100644
index 0000000..6a0d578
--- /dev/null
+++ b/subprojects/core/src/test/groovy/org/gradle/internal/service/scopes/SettingsScopeServicesTest.groovy
@@ -0,0 +1,75 @@
+/*
+ * Copyright 2013 the original author or authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.gradle.internal.service.scopes
+
+import org.gradle.api.internal.SettingsInternal
+import org.gradle.api.internal.file.BaseDirFileResolver
+import org.gradle.api.internal.file.FileResolver
+import org.gradle.api.internal.plugins.DefaultPluginContainer
+import org.gradle.api.internal.plugins.PluginRegistry
+import org.gradle.api.plugins.PluginContainer
+import org.gradle.internal.service.ServiceRegistry
+import spock.lang.Specification
+
+import static org.hamcrest.Matchers.sameInstance
+
+class SettingsScopeServicesTest extends Specification {
+    private SettingsInternal settings = Mock()
+    private ServiceRegistry parent = Stub()
+    private SettingsScopeServices registry = new SettingsScopeServices(parent, settings)
+    private PluginRegistry pluginRegistryParent = Mock()
+    private PluginRegistry pluginRegistryChild = Mock()
+
+    def setup() {
+        settings.getSettingsDir() >> new File("settings-dir").absoluteFile
+        parent.get(org.gradle.internal.nativeplatform.filesystem.FileSystem) >> Stub(org.gradle.internal.nativeplatform.filesystem.FileSystem)
+        parent.get(PluginRegistry) >> pluginRegistryParent
+        pluginRegistryParent.createChild(_, _) >> pluginRegistryChild
+    }
+
+
+    def "provides a plugin container"() {
+        when:
+        def pluginContainer = registry.get(PluginContainer)
+        def secondPluginContainer = registry.get(PluginContainer)
+
+        then:
+        pluginContainer instanceof DefaultPluginContainer
+        secondPluginContainer sameInstance(pluginContainer)
+    }
+
+    def "provides a file resolver"() {
+        when:
+        def fileResolver = registry.get(FileResolver)
+        def secondFileResolver = registry.get(FileResolver)
+
+        then:
+        fileResolver instanceof BaseDirFileResolver
+        secondFileResolver sameInstance(fileResolver)
+    }
+
+
+    def "provides a plugin registry"() {
+        when:
+        def pluginRegistry = registry.get(PluginRegistry)
+        def secondPluginRegistry = registry.get(PluginRegistry)
+
+        then:
+        pluginRegistry == pluginRegistryChild
+        secondPluginRegistry sameInstance(pluginRegistry)
+    }
+}
diff --git a/subprojects/core/src/test/groovy/org/gradle/internal/service/scopes/TaskExecutionServicesTest.groovy b/subprojects/core/src/test/groovy/org/gradle/internal/service/scopes/TaskExecutionServicesTest.groovy
new file mode 100644
index 0000000..69cdfaf
--- /dev/null
+++ b/subprojects/core/src/test/groovy/org/gradle/internal/service/scopes/TaskExecutionServicesTest.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.internal.service.scopes
+
+import org.gradle.StartParameter
+import org.gradle.api.internal.tasks.TaskExecuter
+import org.gradle.api.internal.tasks.execution.ExecuteAtMostOnceTaskExecuter
+import org.gradle.api.invocation.Gradle
+import org.gradle.cache.CacheRepository
+import org.gradle.cache.DirectoryCacheBuilder
+import org.gradle.cache.PersistentCache
+import org.gradle.internal.reflect.Instantiator
+import org.gradle.internal.service.ServiceRegistry
+import org.gradle.listener.ListenerManager
+import spock.lang.Specification
+
+class TaskExecutionServicesTest extends Specification {
+    final ServiceRegistry parent = Mock()
+    final Gradle gradle = Mock()
+    final TaskExecutionServices services = new TaskExecutionServices(parent, gradle)
+
+    def "makes a TaskExecutor available"() {
+        given:
+        ListenerManager listenerManager = Mock()
+        StartParameter startParameter = Mock()
+        CacheRepository cacheRepository = Mock()
+        Instantiator instantiator = Mock();
+        DirectoryCacheBuilder cacheBuilder = Mock()
+        PersistentCache cache = Mock()
+        _ * parent.get(ListenerManager) >> listenerManager
+        _ * parent.get(StartParameter) >> startParameter
+        _ * parent.get(CacheRepository) >> cacheRepository
+        _ * parent.get(Instantiator) >> instantiator
+        _ * cacheRepository.cache(!null) >> cacheBuilder
+        _ * cacheBuilder.forObject(gradle) >> cacheBuilder
+        _ * cacheBuilder.withDisplayName(!null) >> cacheBuilder
+        _ * cacheBuilder.withLockMode(!null) >> cacheBuilder
+        _ * cacheBuilder.open() >> cache
+
+        expect:
+        services.get(TaskExecuter) instanceof ExecuteAtMostOnceTaskExecuter
+        services.get(TaskExecuter).is(services.get(TaskExecuter))
+    }
+}
diff --git a/subprojects/core/src/test/groovy/org/gradle/internal/service/scopes/TaskScopeServicesTest.java b/subprojects/core/src/test/groovy/org/gradle/internal/service/scopes/TaskScopeServicesTest.java
new file mode 100644
index 0000000..eda529d
--- /dev/null
+++ b/subprojects/core/src/test/groovy/org/gradle/internal/service/scopes/TaskScopeServicesTest.java
@@ -0,0 +1,91 @@
+/*
+ * Copyright 2010 the original author or authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.gradle.internal.service.scopes;
+
+import org.gradle.api.internal.TaskInternal;
+import org.gradle.api.internal.TaskOutputsInternal;
+import org.gradle.api.internal.file.FileResolver;
+import org.gradle.api.internal.project.ProjectInternal;
+import org.gradle.api.internal.tasks.DefaultTaskInputs;
+import org.gradle.api.internal.tasks.DefaultTaskOutputs;
+import org.gradle.api.internal.tasks.TaskStatusNagger;
+import org.gradle.api.logging.LoggingManager;
+import org.gradle.api.tasks.TaskInputs;
+import org.gradle.internal.Factory;
+import org.gradle.internal.service.ServiceRegistry;
+import org.gradle.logging.LoggingManagerInternal;
+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 junit.framework.Assert.assertSame;
+import static org.hamcrest.Matchers.instanceOf;
+import static org.hamcrest.Matchers.sameInstance;
+import static org.junit.Assert.assertThat;
+
+ at RunWith(JMock.class)
+public class TaskScopeServicesTest {
+    private final JUnit4Mockery context = new JUnit4Mockery();
+    private final ServiceRegistry parent = context.mock(ServiceRegistry.class);
+    private final ProjectInternal project = context.mock(ProjectInternal.class);
+    private final TaskInternal task = context.mock(TaskInternal.class);
+    private final TaskScopeServices registry = new TaskScopeServices(parent, project, task);
+
+    @Before
+    public void setUp() {
+        context.checking(new Expectations() {{
+            allowing(project).getFileResolver();
+            will(returnValue(context.mock(FileResolver.class)));
+        }});
+    }
+
+    @Test
+    public void createsATaskInputsInstance() {
+        TaskInputs inputs = registry.get(TaskInputs.class);
+        assertThat(inputs, instanceOf(DefaultTaskInputs.class));
+    }
+
+    @Test
+    public void createsATaskOutputsInternalInstance() {
+        TaskOutputsInternal outputs = registry.get(TaskOutputsInternal.class);
+        assertThat(outputs, instanceOf(DefaultTaskOutputs.class));
+    }
+
+    @Test
+    public void createsATaskStatusNaggerInstance() {
+        TaskStatusNagger nagger = registry.get(TaskStatusNagger.class);
+        assertSame(nagger, registry.get(TaskStatusNagger.class));
+    }
+
+    @Test
+    public void createsALoggingManagerAndStdOutputCapture() {
+        final Factory<LoggingManagerInternal> loggingManagerFactory = context.mock(Factory.class);
+        final LoggingManager loggingManager = context.mock(LoggingManagerInternal.class);
+
+        context.checking(new Expectations() {{
+            allowing(parent).getFactory(LoggingManagerInternal.class);
+            will(returnValue(loggingManagerFactory));
+            one(loggingManagerFactory).create();
+            will(returnValue(loggingManager));
+        }});
+
+        assertThat(registry.get(LoggingManager.class), sameInstance(loggingManager));
+    }
+}
diff --git a/subprojects/core/src/test/groovy/org/gradle/invocation/DefaultBuildClassLoaderRegistryTest.groovy b/subprojects/core/src/test/groovy/org/gradle/invocation/DefaultBuildClassLoaderRegistryTest.groovy
new file mode 100644
index 0000000..1ea1697
--- /dev/null
+++ b/subprojects/core/src/test/groovy/org/gradle/invocation/DefaultBuildClassLoaderRegistryTest.groovy
@@ -0,0 +1,49 @@
+/*
+ * Copyright 2013 the original author or authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.gradle.invocation
+
+import org.gradle.initialization.ClassLoaderRegistry
+import org.gradle.internal.classloader.CachingClassLoader
+import org.gradle.internal.classloader.MultiParentClassLoader
+import spock.lang.Specification
+
+class DefaultBuildClassLoaderRegistryTest extends Specification {
+    final globalRegistry = Mock(ClassLoaderRegistry)
+
+    def "wires up build classloaders"() {
+        def rootClassLoader = Mock(ClassLoader)
+        def additionalClassLoader = Mock(ClassLoader)
+
+        given:
+        globalRegistry.gradleApiClassLoader >> rootClassLoader
+
+        when:
+        def registry = new DefaultBuildClassLoaderRegistry(globalRegistry)
+        def scriptLoader = registry.scriptClassLoader
+
+        then:
+        scriptLoader instanceof CachingClassLoader
+        scriptLoader.parent instanceof MultiParentClassLoader
+        scriptLoader.parent.parents == [rootClassLoader]
+
+        when:
+        registry.addRootClassLoader(additionalClassLoader)
+
+        then:
+        scriptLoader.parent.parents == [rootClassLoader, additionalClassLoader]
+    }
+}
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 f9c084e..355b2ee 100644
--- a/subprojects/core/src/test/groovy/org/gradle/invocation/DefaultGradleTest.java
+++ b/subprojects/core/src/test/groovy/org/gradle/invocation/DefaultGradleTest.java
@@ -24,20 +24,23 @@ import org.gradle.api.Project;
 import org.gradle.api.ProjectEvaluationListener;
 import org.gradle.api.initialization.dsl.ScriptHandler;
 import org.gradle.api.internal.GradleDistributionLocator;
+import org.gradle.api.internal.file.FileResolver;
 import org.gradle.api.internal.initialization.ScriptClassLoaderProvider;
 import org.gradle.api.internal.plugins.PluginRegistry;
-import org.gradle.api.internal.project.IProjectRegistry;
+import org.gradle.api.internal.project.ProjectRegistry;
 import org.gradle.api.internal.project.ProjectInternal;
-import org.gradle.api.internal.project.ServiceRegistryFactory;
+import org.gradle.internal.service.scopes.ServiceRegistryFactory;
 import org.gradle.api.invocation.Gradle;
+import org.gradle.api.plugins.PluginContainer;
+import org.gradle.configuration.ScriptPluginFactory;
 import org.gradle.execution.TaskGraphExecuter;
 import org.gradle.listener.ClosureBackedMethodInvocationDispatch;
 import org.gradle.listener.ListenerBroadcast;
 import org.gradle.listener.ListenerManager;
 import org.gradle.util.GradleVersion;
-import org.gradle.util.HelperUtil;
+import org.gradle.util.TestUtil;
 import org.gradle.util.JUnit4GroovyMockery;
-import org.gradle.util.MultiParentClassLoader;
+import org.gradle.internal.classloader.MultiParentClassLoader;
 import org.jmock.Expectations;
 import org.jmock.integration.junit4.JMock;
 import org.jmock.integration.junit4.JUnit4Mockery;
@@ -60,7 +63,7 @@ public class DefaultGradleTest {
     private final ScriptHandler scriptHandlerMock = context.mock(ScriptHandler.class);
     private final ServiceRegistryFactory serviceRegistryFactoryMock = context.mock(ServiceRegistryFactory.class, "parent");
     private final ServiceRegistryFactory gradleServiceRegistryMock = context.mock(ServiceRegistryFactory.class, "gradle");
-    private final IProjectRegistry projectRegistry = context.mock(IProjectRegistry.class);
+    private final ProjectRegistry projectRegistry = context.mock(ProjectRegistry.class);
     private final PluginRegistry pluginRegistry = context.mock(PluginRegistry.class);
     private final TaskGraphExecuter taskExecuter = context.mock(TaskGraphExecuter.class);
     private final ListenerManager listenerManager = context.mock(ListenerManager.class);
@@ -69,6 +72,10 @@ public class DefaultGradleTest {
     private final GradleDistributionLocator gradleDistributionLocatorMock = context.mock(GradleDistributionLocator.class);
     private final ListenerBroadcast<BuildListener> buildListenerBroadcast = new ListenerBroadcast<BuildListener>(BuildListener.class);
     private final ListenerBroadcast<ProjectEvaluationListener> projectEvaluationListenerBroadcast = context.mock(ListenerBroadcast.class);
+    private final FileResolver fileResolverMock = context.mock(FileResolver.class);
+    private final PluginContainer pluginContainer = context.mock(PluginContainer.class);
+    private final ScriptPluginFactory scriptPluginFactory= context.mock(ScriptPluginFactory.class);
+
     private DefaultGradle gradle;
 
     @Before
@@ -80,7 +87,7 @@ public class DefaultGradleTest {
             will(returnValue(scriptHandlerMock));
             allowing(gradleServiceRegistryMock).get(ScriptClassLoaderProvider.class);
             will(returnValue(context.mock(ScriptClassLoaderProvider.class)));
-            allowing(gradleServiceRegistryMock).get(IProjectRegistry.class);
+            allowing(gradleServiceRegistryMock).get(ProjectRegistry.class);
             will(returnValue(projectRegistry));
             allowing(gradleServiceRegistryMock).get(PluginRegistry.class);
             will(returnValue(pluginRegistry));
@@ -92,6 +99,12 @@ public class DefaultGradleTest {
             will(returnValue(scriptClassLoaderMock));
             allowing(gradleServiceRegistryMock).get(GradleDistributionLocator.class);
             will(returnValue(gradleDistributionLocatorMock));
+            allowing(gradleServiceRegistryMock).get(PluginContainer.class);
+            will(returnValue(pluginContainer));
+            allowing(gradleServiceRegistryMock).get(FileResolver.class);
+            will(returnValue(fileResolverMock));
+            allowing(gradleServiceRegistryMock).get(ScriptPluginFactory.class);
+            will(returnValue(scriptPluginFactory));
             allowing(listenerManager).createAnonymousBroadcaster(BuildListener.class);
             will(returnValue(buildListenerBroadcast));
             allowing(listenerManager).createAnonymousBroadcaster(ProjectEvaluationListener.class);
@@ -145,7 +158,7 @@ public class DefaultGradleTest {
 
     @Test
     public void broadcastsBeforeProjectEvaluateEventsToClosures() {
-        final Closure closure = HelperUtil.TEST_CLOSURE;
+        final Closure closure = TestUtil.TEST_CLOSURE;
         context.checking(new Expectations() {{
             one(projectEvaluationListenerBroadcast).add(new ClosureBackedMethodInvocationDispatch("beforeEvaluate", closure));
         }});
@@ -155,7 +168,7 @@ public class DefaultGradleTest {
 
     @Test
     public void broadcastsAfterProjectEvaluateEventsToClosures() {
-        final Closure closure = HelperUtil.TEST_CLOSURE;
+        final Closure closure = TestUtil.TEST_CLOSURE;
         context.checking(new Expectations() {{
             one(projectEvaluationListenerBroadcast).add(new ClosureBackedMethodInvocationDispatch("afterEvaluate", closure));
         }});
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 ebc82e4..920e5c3 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
@@ -25,11 +25,9 @@ import org.junit.Rule;
 import org.junit.Test;
 import org.junit.runner.RunWith;
 
-import static org.junit.Assert.*;
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertNull;
 
-/**
- * @author Hans Dockter
- */
 @RunWith(JMock.class)
 public class DefaultLoggingManagerTest {
     @Rule
diff --git a/subprojects/core/src/test/groovy/org/gradle/logging/internal/LoggingCommandLineConverterTest.groovy b/subprojects/core/src/test/groovy/org/gradle/logging/internal/LoggingCommandLineConverterTest.groovy
index 0ee9f1e..e77d3cf 100644
--- a/subprojects/core/src/test/groovy/org/gradle/logging/internal/LoggingCommandLineConverterTest.groovy
+++ b/subprojects/core/src/test/groovy/org/gradle/logging/internal/LoggingCommandLineConverterTest.groovy
@@ -61,7 +61,7 @@ class LoggingCommandLineConverterTest extends Specification {
     }
 
     def convertsShowStacktrace() {
-        expectedConfig.showStacktrace = ShowStacktrace.ALWAYS;
+        expectedConfig.showStacktrace = ShowStacktrace.ALWAYS
 
         expect:
         checkConversion(['-s'])
@@ -69,17 +69,25 @@ class LoggingCommandLineConverterTest extends Specification {
     }
 
     def convertsShowFullStacktrace() {
-        expectedConfig.showStacktrace = ShowStacktrace.ALWAYS_FULL;
+        expectedConfig.showStacktrace = ShowStacktrace.ALWAYS_FULL
 
         expect:
         checkConversion(['-S'])
         checkConversion(['--full-stacktrace'])
     }
 
+    def usesLastLogLevelAndStacktraceOption() {
+        expectedConfig.showStacktrace = ShowStacktrace.ALWAYS_FULL
+        expectedConfig.logLevel = LogLevel.QUIET
+
+        expect:
+        checkConversion(['-s', '--debug', '-q', '--full-stacktrace'])
+    }
+
     def providesLogLevelOptions() {
         expect:
-        converter.logLevelOptions.containsAll(["d", "q", "i"])
-        converter.logLevelOptions.size() == 3
+        converter.logLevelOptions == ["d", "q", "i"] as Set
+        converter.logLevels == [LogLevel.DEBUG, LogLevel.INFO, LogLevel.LIFECYCLE, LogLevel.QUIET] as Set
     }
 
     void checkConversion(List<String> args) {
diff --git a/subprojects/core/src/test/groovy/org/gradle/model/internal/PersistentModelObjectRegistryTest.groovy b/subprojects/core/src/test/groovy/org/gradle/model/internal/PersistentModelObjectRegistryTest.groovy
new file mode 100644
index 0000000..8860477
--- /dev/null
+++ b/subprojects/core/src/test/groovy/org/gradle/model/internal/PersistentModelObjectRegistryTest.groovy
@@ -0,0 +1,166 @@
+/*
+ * Copyright 2013 the original author or authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.gradle.model.internal
+
+import org.gradle.model.ModelType
+import org.gradle.test.fixtures.file.TestNameTestDirectoryProvider
+import org.junit.Rule
+import spock.lang.Ignore
+import spock.lang.Specification
+
+class PersistentModelObjectRegistryTest extends Specification {
+    @Rule TestNameTestDirectoryProvider tmpDir = new TestNameTestDirectoryProvider()
+
+    def "persists the public properties of a Groovy model object"() {
+        def model = new GroovyTestModel(prop1: "value", prop2: 12, prop3: ["a", "b", "c"])
+        def storeFile = tmpDir.file("model.bin")
+
+        when:
+        def writeRegistry = new PersistentModelObjectRegistry(storeFile)
+        writeRegistry.put("a", model)
+        writeRegistry.close()
+
+        def readRegistry = new PersistentModelObjectRegistry(storeFile)
+        def result = readRegistry.get("a", GroovyTestModel)
+        readRegistry.close()
+
+        then:
+        result instanceof GroovyTestModel
+        result != model
+        result.prop1 == model.prop1
+        result.prop2 == model.prop2
+        result.prop3 == model.prop3
+    }
+
+    def "persists the public properties of a Java model object"() {
+        def model = new TestModel(prop1: "value", prop2: 12, prop3: ["a", "b", "c"])
+        def storeFile = tmpDir.file("model.bin")
+
+        when:
+        def writeRegistry = new PersistentModelObjectRegistry(storeFile)
+        writeRegistry.put("a", model)
+        writeRegistry.close()
+
+        def readRegistry = new PersistentModelObjectRegistry(storeFile)
+        def result = readRegistry.get("a", TestModel)
+        readRegistry.close()
+
+        then:
+        result instanceof TestModel
+        result != model
+        result.prop1 == model.prop1
+        result.prop2 == model.prop2
+        result.prop3 == model.prop3
+    }
+
+    def "cannot put an object which is not a model object"() {
+        def storeFile = tmpDir.file("model.bin")
+        def registry = new PersistentModelObjectRegistry(storeFile)
+
+        when:
+        registry.put("a", 12)
+
+        then:
+        IllegalArgumentException e = thrown()
+        e.message == 'Cannot persist object of class Integer, as this class is not marked @ModelType'
+
+        cleanup:
+        registry.close()
+    }
+
+    def "model object can reference another model object"() {
+        def storeFile = tmpDir.file("model.bin")
+        def child = new TestModel(prop1: "child")
+        def parent = new ParentModel(child: child, child2: child)
+
+        when:
+        def writeRegistry = new PersistentModelObjectRegistry(storeFile)
+        writeRegistry.put("child", child)
+        writeRegistry.put("parent", parent)
+        writeRegistry.close()
+
+        def readRegistry = new PersistentModelObjectRegistry(storeFile)
+        def result = readRegistry.get("parent", ParentModel)
+        readRegistry.close()
+
+        then:
+        result.child instanceof TestModel
+        result.child.prop1 == "child"
+        result.child.is(result.child2)
+    }
+
+    def "model object instance is reused"() {
+        def storeFile = tmpDir.file("model.bin")
+        def child = new TestModel(prop1: "child")
+        def parent1 = new ParentModel(child: child)
+        def parent2 = new ParentModel(child: child)
+
+        when:
+        def writeRegistry = new PersistentModelObjectRegistry(storeFile)
+        writeRegistry.put("child", child)
+        writeRegistry.put("parent1", parent1)
+        writeRegistry.put("parent2", parent2)
+        writeRegistry.close()
+
+        def readRegistry = new PersistentModelObjectRegistry(storeFile)
+        def result1 = readRegistry.get("parent1", ParentModel)
+        def result2 = readRegistry.get("parent2", ParentModel)
+        readRegistry.close()
+
+        then:
+        result1.child.is(result2.child)
+    }
+
+    def "returns null when object with given identifier is not present"() {
+        def storeFile = tmpDir.file("model.bin")
+        def registry = new PersistentModelObjectRegistry(storeFile)
+
+        expect:
+        registry.get("some-id", TestModel) == null
+
+        cleanup:
+        registry.close()
+    }
+
+    @Ignore
+    def "rethrows failure to get the value of a property"() {
+        expect: false
+    }
+
+    @Ignore
+    def "rethrows failure to construct an instance"() {
+        expect: false
+    }
+
+    @Ignore
+    def "rethrows failure to set the value of a property"() {
+        expect: false
+    }
+
+    @ModelType
+    static class GroovyTestModel {
+        String prop1
+        int prop2
+        List<String> prop3
+    }
+
+    @ModelType
+    static class ParentModel {
+        Object child
+        Object child2
+    }
+}
diff --git a/subprojects/core/src/test/groovy/org/gradle/model/internal/TestModel.java b/subprojects/core/src/test/groovy/org/gradle/model/internal/TestModel.java
new file mode 100644
index 0000000..18489e6
--- /dev/null
+++ b/subprojects/core/src/test/groovy/org/gradle/model/internal/TestModel.java
@@ -0,0 +1,52 @@
+/*
+ * Copyright 2013 the original author or authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.gradle.model.internal;
+
+import org.gradle.model.ModelType;
+
+import java.util.List;
+
+ at ModelType
+public class TestModel {
+    private String prop1;
+    private int prop2;
+    private List<String> prop3;
+
+    public String getProp1() {
+        return prop1;
+    }
+
+    public void setProp1(String prop1) {
+        this.prop1 = prop1;
+    }
+
+    public int getProp2() {
+        return prop2;
+    }
+
+    public void setProp2(int prop2) {
+        this.prop2 = prop2;
+    }
+
+    public List<String> getProp3() {
+        return prop3;
+    }
+
+    public void setProp3(List<String> prop3) {
+        this.prop3 = prop3;
+    }
+}
diff --git a/subprojects/core/src/test/groovy/org/gradle/process/internal/DefaultExecHandleSpec.groovy b/subprojects/core/src/test/groovy/org/gradle/process/internal/DefaultExecHandleSpec.groovy
index 8707311..93f9470 100644
--- a/subprojects/core/src/test/groovy/org/gradle/process/internal/DefaultExecHandleSpec.groovy
+++ b/subprojects/core/src/test/groovy/org/gradle/process/internal/DefaultExecHandleSpec.groovy
@@ -28,9 +28,6 @@ import spock.lang.Timeout
 
 import java.util.concurrent.Callable
 
-/**
- * @author Tom Eyckmans, Szczepan Faber
- */
 @Timeout(60)
 class DefaultExecHandleSpec extends Specification {
     @Rule final TestNameTestDirectoryProvider tmpDir = new TestNameTestDirectoryProvider();
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 2265f3c..e7d06f9 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
@@ -82,7 +82,7 @@ class DefaultWorkerProcessTest extends MultithreadedTestCase {
                 workerProcess.start()
                 fail()
             } catch (ExecException e) {
-                assertThat(e.message, equalTo("Timeout after waiting 1.0 seconds for $execHandle (STARTED, running: true) to connect." as String))
+                assertThat(e.message, equalTo(String.format("Timeout after waiting %.1f seconds for $execHandle (STARTED, running: true) to connect." as String, 1d)))
             }
         }
     }
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 53c3f7f..6431379 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
@@ -13,20 +13,19 @@
  * See the License for the specific language governing permissions and
  * limitations under the License.
  */
-package org.gradle.process.internal;
+package org.gradle.process.internal
 
-
-import org.gradle.api.internal.file.FileResolver
-import org.gradle.api.internal.file.IdentityFileResolver
+import org.gradle.api.internal.file.TestFiles
 import org.gradle.internal.jvm.Jvm
 import spock.lang.Specification
-import static java.util.Arrays.asList
-import java.nio.charset.Charset
 import spock.lang.Unroll
 
+import java.nio.charset.Charset
+
+import static java.util.Arrays.asList
+
 public class JavaExecHandleBuilderTest extends Specification {
-    FileResolver fileResolver = new IdentityFileResolver()
-    JavaExecHandleBuilder builder = new JavaExecHandleBuilder(fileResolver)
+    JavaExecHandleBuilder builder = new JavaExecHandleBuilder(TestFiles.resolver())
     
     public void cannotSetAllJvmArgs() {
         when:
diff --git a/subprojects/core/src/test/groovy/org/gradle/process/internal/JvmOptionsTest.groovy b/subprojects/core/src/test/groovy/org/gradle/process/internal/JvmOptionsTest.groovy
index 9f2837f..b8ba5be 100755
--- a/subprojects/core/src/test/groovy/org/gradle/process/internal/JvmOptionsTest.groovy
+++ b/subprojects/core/src/test/groovy/org/gradle/process/internal/JvmOptionsTest.groovy
@@ -18,14 +18,12 @@
 
 package org.gradle.process.internal
 
-import org.gradle.api.internal.file.IdentityFileResolver
-import spock.lang.Specification
+import org.gradle.api.internal.file.TestFiles
 import org.gradle.process.JavaForkOptions
+import spock.lang.Specification
+
 import java.nio.charset.Charset
 
-/**
- * by Szczepan Faber, created at: 2/13/12
- */
 class JvmOptionsTest extends Specification {
     final String defaultCharset = Charset.defaultCharset().name()
 
@@ -186,7 +184,7 @@ class JvmOptionsTest extends Specification {
     }
 
     private JvmOptions createOpts() {
-        return new JvmOptions(new IdentityFileResolver())
+        return new JvmOptions(TestFiles.resolver())
     }
 
     private JvmOptions parse(String optsString) {
diff --git a/subprojects/core/src/test/groovy/org/gradle/process/internal/child/ImplementationClassLoaderWorkerTest.java b/subprojects/core/src/test/groovy/org/gradle/process/internal/child/ImplementationClassLoaderWorkerTest.java
index 5847570..42ebb24 100644
--- a/subprojects/core/src/test/groovy/org/gradle/process/internal/child/ImplementationClassLoaderWorkerTest.java
+++ b/subprojects/core/src/test/groovy/org/gradle/process/internal/child/ImplementationClassLoaderWorkerTest.java
@@ -20,7 +20,7 @@ import org.gradle.api.Action;
 import org.gradle.api.logging.LogLevel;
 import org.gradle.logging.LoggingManagerInternal;
 import org.gradle.util.JUnit4GroovyMockery;
-import org.gradle.util.MutableURLClassLoader;
+import org.gradle.internal.classloader.MutableURLClassLoader;
 import org.jmock.Expectations;
 import org.jmock.integration.junit4.JMock;
 import org.jmock.integration.junit4.JUnit4Mockery;
diff --git a/subprojects/core/src/test/groovy/org/gradle/process/internal/child/SerializableMockHelper.groovy b/subprojects/core/src/test/groovy/org/gradle/process/internal/child/SerializableMockHelper.groovy
index 252a31a..5061b8c 100644
--- a/subprojects/core/src/test/groovy/org/gradle/process/internal/child/SerializableMockHelper.groovy
+++ b/subprojects/core/src/test/groovy/org/gradle/process/internal/child/SerializableMockHelper.groovy
@@ -20,17 +20,15 @@ package org.gradle.process.internal.child
 
 import groovyjarjarasm.asm.ClassVisitor
 import groovyjarjarasm.asm.ClassWriter
-import java.util.concurrent.atomic.AtomicInteger
 import org.codehaus.groovy.ast.ClassNode
 import org.codehaus.groovy.control.CompilationUnit
 import org.codehaus.groovy.control.CompilationUnit.ClassgenCallback
 import org.codehaus.groovy.control.Phases
 import org.gradle.api.Action
 
-class SerializableMockHelper { /**
- * @author Hans Dockter
- */
-    static final Map ACTIONS = [:]
+import java.util.concurrent.atomic.AtomicInteger
+
+class SerializableMockHelper {     static final Map ACTIONS = [:]
     private final AtomicInteger counter = new AtomicInteger()
 
     /**
diff --git a/subprojects/core/src/test/groovy/org/gradle/profile/BuildProfileTest.groovy b/subprojects/core/src/test/groovy/org/gradle/profile/BuildProfileTest.groovy
index 68a51da..2d6ac7c 100644
--- a/subprojects/core/src/test/groovy/org/gradle/profile/BuildProfileTest.groovy
+++ b/subprojects/core/src/test/groovy/org/gradle/profile/BuildProfileTest.groovy
@@ -15,52 +15,82 @@
  */
 package org.gradle.profile
 
+import org.gradle.StartParameter
+import org.gradle.api.tasks.TaskState
 import spock.lang.Specification
-import org.gradle.api.invocation.Gradle
-import org.gradle.api.artifacts.ResolvableDependencies
-import org.gradle.api.Project
 
 class BuildProfileTest extends Specification {
-    final Gradle gradle = Mock()
-    final BuildProfile profile = new BuildProfile(gradle)
+    private profile = new BuildProfile(new StartParameter())
 
     def "creates dependency set profile on first get"() {
+        expect:
+        def dependencyProfile = profile.getDependencySetProfile("path")
+        dependencyProfile != null
+        profile.getDependencySetProfile("path") == dependencyProfile
+    }
+
+    def "provides sorted dependency set profiles"() {
         given:
-        ResolvableDependencies deps = dependencySet("path")
+        def a = profile.getDependencySetProfile("a").setStart(100).setFinish(200)
+        def b = profile.getDependencySetProfile("b").setStart(200).setFinish(400)
+        def c = profile.getDependencySetProfile("c").setStart(400).setFinish(600)
+        def d = profile.getDependencySetProfile("d").setStart(600).setFinish(601)
 
         expect:
-        def dependencyProfile = profile.getDependencySetProfile(deps)
-        dependencyProfile != null
-        profile.getDependencySetProfile(deps) == dependencyProfile
+        profile.dependencySets.operations == [b, c, a, d]
     }
 
-    def "can get all dependency set profiles"() {
+    def "provides sorted configuration profiles"() {
         given:
-        def a = profile.getDependencySetProfile(dependencySet("a"))
-        def b = profile.getDependencySetProfile(dependencySet("b"))
+        def a = profile.getProjectProfile("a").configurationOperation.setStart(100).setFinish(200)
+        def b = profile.getProjectProfile("b").configurationOperation.setStart(200).setFinish(500)
+        def c = profile.getProjectProfile("c").configurationOperation.setStart(500).setFinish(800)
+        def d = profile.getProjectProfile("d").configurationOperation.setStart(800).setFinish(850)
 
         expect:
-        profile.dependencySets.operations == [a, b]
+        profile.projectConfiguration.operations == [b, c, a, d]
     }
 
-    def "can get all project configuration profiles"() {
+    def "provides sorted project profiles"() {
         given:
-        def a = profile.getProjectProfile(project("a"))
-        def b = profile.getProjectProfile(project("b"))
+        profile.getProjectProfile("a").getTaskProfile("a:x").completed(Stub(TaskState)).setStart(100).setFinish(300)
+        profile.getProjectProfile("b").getTaskProfile("b:x").completed(Stub(TaskState)).setStart(300).setFinish(300)
+        profile.getProjectProfile("c").getTaskProfile("c:x").completed(Stub(TaskState)).setStart(300).setFinish(300)
+        profile.getProjectProfile("d").getTaskProfile("d:x").completed(Stub(TaskState)).setStart(301).setFinish(302)
 
         expect:
-        profile.projectConfiguration.operations == [a.evaluation, b.evaluation]
+        profile.projects == [profile.getProjectProfile("a"), profile.getProjectProfile("d"), profile.getProjectProfile("b"), profile.getProjectProfile("c")]
+    }
+
+    def "contains build description"() {
+        given:
+        def param = new StartParameter()
+        param.setTaskNames(["foo", "bar"])
+        param.setExcludedTaskNames(["one", "two"])
+
+        when:
+        profile = new BuildProfile(param)
+
+        then:
+        profile.buildDescription.contains(" -x one -x two foo bar")
     }
 
-    def dependencySet(String path) {
-        ResolvableDependencies dependencies = Mock()
-        _ * dependencies.path >> path
-        return dependencies
+    def "build description looks nice even if no tasks specified"() {
+        given:
+        def param = new StartParameter()
+
+        when:
+        profile = new BuildProfile(param)
+
+        then:
+        profile.buildDescription.contains(" (no tasks specified)")
     }
 
-    def project(String path) {
-        Project project = Mock()
-        _ * project.path >> path
-        return project
+    def "provides start time description"() {
+        when:
+        profile.buildStarted = new GregorianCalendar(2010, 1, 1, 12, 25).getTimeInMillis()
+
+        then:
+        profile.buildStartedDescription == "Started on: 2010/02/01 - 12:25:00"
     }
 }
diff --git a/subprojects/core/src/test/groovy/org/gradle/profile/ProfileReportRendererTest.groovy b/subprojects/core/src/test/groovy/org/gradle/profile/ProfileReportRendererTest.groovy
new file mode 100644
index 0000000..dd95aa3
--- /dev/null
+++ b/subprojects/core/src/test/groovy/org/gradle/profile/ProfileReportRendererTest.groovy
@@ -0,0 +1,227 @@
+/*
+ * Copyright 2013 the original author or authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.gradle.profile
+
+import org.gradle.StartParameter
+import org.gradle.api.tasks.TaskState
+import org.gradle.test.fixtures.file.TestNameTestDirectoryProvider
+import org.junit.Rule
+import spock.lang.Specification
+
+import static org.gradle.util.TextUtil.toPlatformLineSeparators
+
+class ProfileReportRendererTest extends Specification {
+
+    @Rule TestNameTestDirectoryProvider temp = new TestNameTestDirectoryProvider()
+
+    def "renders report"() {
+        def model = new BuildProfile(new StartParameter())
+        def file = temp.file("report.html")
+
+        model.profilingStarted   = time(12, 20, 0)
+        model.buildStarted       = time(12, 20, 0, 700)
+        model.settingsEvaluated  = time(12, 20, 3)
+        model.projectsLoaded     = time(12, 20, 6)
+
+        model.buildFinished      = time(12, 35, 30)
+
+        model.getDependencySetProfile("compile").start = time(12, 22, 0)
+        model.getDependencySetProfile("compile").finish = time(12, 23, 30)
+
+        model.getDependencySetProfile("runtime").start = time(12, 24, 0)
+        model.getDependencySetProfile("runtime").finish = time(12, 24, 30)
+
+        model.getProjectProfile("a").configurationOperation.start = time(12, 20, 7)
+        model.getProjectProfile("a").configurationOperation.finish = time(12, 20, 10)
+        model.getProjectProfile("a").getTaskProfile("a:foo").completed(Stub(TaskState)).setStart(time(12, 25, 0)).setFinish(time(12, 26, 30))
+        model.getProjectProfile("a").getTaskProfile("a:bar").completed(Stub(TaskState)).setStart(time(12, 26, 30)).setFinish(time(12, 27, 0))
+
+        model.getProjectProfile("b").configurationOperation.start = time(12, 20, 10)
+        model.getProjectProfile("b").configurationOperation.finish = time(12, 20, 15)
+        //let's say they run in parallel, hence same start time
+        model.getProjectProfile("b").getTaskProfile("b:foo").completed(Stub(TaskState)).setStart(time(12, 27, 0)).setFinish(time(12, 29, 30))
+        model.getProjectProfile("b").getTaskProfile("b:bar").completed(Stub(TaskState)).setStart(time(12, 27, 0)).setFinish(time(12, 29, 0))
+
+        when:
+        new ProfileReportRenderer().writeTo(model, file)
+
+        then:
+        println file
+        println file.text
+        file.text.contains(toPlatformLineSeparators("""<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01//EN" "http://www.w3.org/TR/html4/strict.dtd">
+<html>
+<head>
+<meta httpEquiv="Content-Type" content="text/html; charset=utf-8"/>
+<title>Profile report</title>
+<link href="base-style.css" rel="stylesheet" type="text/css"/>
+<link href="style.css" rel="stylesheet" type="text/css"/>
+<script src="report.js" type="text/javascript"></script>
+</head>
+<body>
+<div id="content">
+<h1>Profile report</h1>
+<div id="header">
+<p>Profiled build: (no tasks specified)</p>
+<p>Started on: 2010/02/05 - 12:20:00</p>
+</div>
+<div id="tabs">
+<ul class="tabLinks">
+<li>
+<a href="#tab0">Summary</a>
+</li>
+<li>
+<a href="#tab1">Configuration</a>
+</li>
+<li>
+<a href="#tab2">Dependency Resolution</a>
+</li>
+<li>
+<a href="#tab3">Task Execution</a>
+</li>
+</ul>
+<div class="tab" id="tab0">
+<h2>Summary</h2>
+<table>
+<thead>
+<tr>
+<th>Description</th>
+<th class="numeric">Duration</th>
+</tr>
+</thead>
+<tr>
+<td>Total Build Time</td>
+<td class="numeric">15m30.00s</td>
+</tr>
+<tr>
+<td>Startup</td>
+<td class="numeric">0.700s</td>
+</tr>
+<tr>
+<td>Settings and BuildSrc</td>
+<td class="numeric">2.300s</td>
+</tr>
+<tr>
+<td>Loading Projects</td>
+<td class="numeric">3.000s</td>
+</tr>
+<tr>
+<td>Configuring Projects</td>
+<td class="numeric">8.000s</td>
+</tr>
+<tr>
+<td>Task Execution</td>
+<td class="numeric">6m30.00s</td>
+</tr>
+</table>
+</div>
+<div class="tab" id="tab1">
+<h2>Configuration</h2>
+<table>
+<thead>
+<tr>
+<th>Project</th>
+<th class="numeric">Duration</th>
+</tr>
+</thead>
+<tr>
+<td>All projects</td>
+<td class="numeric">8.000s</td>
+</tr>
+<tr>
+<td>b</td>
+<td class="numeric">5.000s</td>
+</tr>
+<tr>
+<td>a</td>
+<td class="numeric">3.000s</td>
+</tr>
+</table>
+</div>
+<div class="tab" id="tab2">
+<h2>Dependency Resolution</h2>
+<table>
+<thead>
+<tr>
+<th>Dependencies</th>
+<th class="numeric">Duration</th>
+</tr>
+</thead>
+<tr>
+<td>All dependencies</td>
+<td class="numeric">2m0.00s</td>
+</tr>
+<tr>
+<td>compile</td>
+<td class="numeric">1m30.00s</td>
+</tr>
+<tr>
+<td>runtime</td>
+<td class="numeric">30.000s</td>
+</tr>
+</table>
+</div>
+<div class="tab" id="tab3">
+<h2>Task Execution</h2>
+<table>
+<thead>
+<tr>
+<th>Task</th>
+<th class="numeric">Duration</th>
+<th>Result</th>
+</tr>
+</thead>
+<tr>
+<td>b</td>
+<td class="numeric">4m30.00s</td>
+<td>(total)</td>
+</tr>
+<tr>
+<td class="indentPath">b:foo</td>
+<td class="numeric">2m30.00s</td>
+<td>Did No Work</td>
+</tr>
+<tr>
+<td class="indentPath">b:bar</td>
+<td class="numeric">2m0.00s</td>
+<td>Did No Work</td>
+</tr>
+<tr>
+<td>a</td>
+<td class="numeric">2m0.00s</td>
+<td>(total)</td>
+</tr>
+<tr>
+<td class="indentPath">a:foo</td>
+<td class="numeric">1m30.00s</td>
+<td>Did No Work</td>
+</tr>
+<tr>
+<td class="indentPath">a:bar</td>
+<td class="numeric">30.000s</td>
+<td>Did No Work</td>
+</tr>
+</table>
+</div>
+</div>"""))
+    }
+
+    private long time(int hour, int mins, int secs, int ms = 0) {
+        def cal = new GregorianCalendar(2010, 1, 5, hour, mins, secs)
+        cal.add(Calendar.MILLISECOND, ms)
+        cal.getTimeInMillis()
+    }
+}
diff --git a/subprojects/core/src/test/groovy/org/gradle/profile/ProjectProfileTest.groovy b/subprojects/core/src/test/groovy/org/gradle/profile/ProjectProfileTest.groovy
new file mode 100644
index 0000000..a737dfa
--- /dev/null
+++ b/subprojects/core/src/test/groovy/org/gradle/profile/ProjectProfileTest.groovy
@@ -0,0 +1,34 @@
+/*
+ * Copyright 2013 the original author or authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.gradle.profile
+
+import org.gradle.api.tasks.TaskState
+import spock.lang.Specification
+
+class ProjectProfileTest extends Specification {
+
+    def "provides sorted tasks"() {
+        def profile = new ProjectProfile(":foo")
+        def a = profile.getTaskProfile("foo:a").completed(Stub(TaskState)).setStart(100).setFinish(300)
+        def b = profile.getTaskProfile("foo:b").completed(Stub(TaskState)).setStart(300).setFinish(300)
+        def c = profile.getTaskProfile("foo:c").completed(Stub(TaskState)).setStart(300).setFinish(300)
+        def d = profile.getTaskProfile("foo:d").completed(Stub(TaskState)).setStart(301).setFinish(302)
+
+        expect:
+        profile.tasks.operations == [a, d, b, c]
+    }
+}
diff --git a/subprojects/core/src/test/groovy/org/gradle/profile/TaskExecutionTest.groovy b/subprojects/core/src/test/groovy/org/gradle/profile/TaskExecutionTest.groovy
new file mode 100644
index 0000000..ac227ea
--- /dev/null
+++ b/subprojects/core/src/test/groovy/org/gradle/profile/TaskExecutionTest.groovy
@@ -0,0 +1,43 @@
+/*
+ * Copyright 2013 the original author or authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.gradle.profile
+
+import org.gradle.api.tasks.TaskState
+import spock.lang.Specification
+
+class TaskExecutionTest extends Specification {
+
+    def "knows task status"() {
+        def skipped = Stub(TaskState) {
+            getSkipped() >> true
+            getSkipMessage() >> "Skipped for a good reason."
+        }
+        def busy = Stub(TaskState) {
+            getSkipped() >> false
+            getDidWork() >> true
+        }
+        def noWork = Stub(TaskState) {
+            getSkipped() >> false
+            getDidWork() >> false
+        }
+
+        expect:
+        new TaskExecution("a").completed(skipped).status == "Skipped for a good reason."
+        new TaskExecution("a").completed(busy).status == ""
+        new TaskExecution("a").completed(noWork).status == TaskExecution.NO_WORK_MESSAGE
+    }
+}
diff --git a/subprojects/core/src/test/groovy/org/gradle/tooling/provider/model/internal/DefaultToolingModelBuilderRegistryTest.groovy b/subprojects/core/src/test/groovy/org/gradle/tooling/provider/model/internal/DefaultToolingModelBuilderRegistryTest.groovy
new file mode 100644
index 0000000..9fbb7d9
--- /dev/null
+++ b/subprojects/core/src/test/groovy/org/gradle/tooling/provider/model/internal/DefaultToolingModelBuilderRegistryTest.groovy
@@ -0,0 +1,76 @@
+/*
+ * Copyright 2012 the original author or authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS 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.provider.model.internal
+
+import org.gradle.api.internal.project.ProjectInternal
+import org.gradle.tooling.provider.model.ToolingModelBuilder
+import org.gradle.tooling.provider.model.UnknownModelException
+import spock.lang.Specification
+
+class DefaultToolingModelBuilderRegistryTest extends Specification {
+    final def registy = new DefaultToolingModelBuilderRegistry()
+
+    def "finds model builder for requested model"() {
+        def builder1 = Mock(ToolingModelBuilder)
+        def builder2 = Mock(ToolingModelBuilder)
+
+        given:
+        registy.register(builder1)
+        registy.register(builder2)
+
+        and:
+        builder1.canBuild("model") >> false
+        builder2.canBuild("model") >> true
+
+        expect:
+        registy.getBuilder("model") == builder2
+    }
+
+    def "includes a simple implementation for the Void model"() {
+        expect:
+        registy.getBuilder(Void.class.name).buildAll(Void.class.name, Mock(ProjectInternal)) == null
+    }
+
+    def "fails when no builder is available for requested model"() {
+        when:
+        registy.getBuilder("model")
+
+        then:
+        UnknownModelException e = thrown()
+        e.message == "No builders are available to build a model of type 'model'."
+    }
+
+    def "fails when multiple builders are available for requested model"() {
+        def builder1 = Mock(ToolingModelBuilder)
+        def builder2 = Mock(ToolingModelBuilder)
+
+        given:
+        registy.register(builder1)
+        registy.register(builder2)
+
+        and:
+        builder1.canBuild("model") >> true
+        builder2.canBuild("model") >> true
+
+        when:
+        registy.getBuilder("model")
+
+        then:
+        UnsupportedOperationException e = thrown()
+        e.message == "Multiple builders are available to build a model of type 'model'."
+    }
+}
diff --git a/subprojects/core/src/test/groovy/org/gradle/util/DefaultClassLoaderFactoryTest.groovy b/subprojects/core/src/test/groovy/org/gradle/util/DefaultClassLoaderFactoryTest.groovy
deleted file mode 100644
index 38737a6..0000000
--- a/subprojects/core/src/test/groovy/org/gradle/util/DefaultClassLoaderFactoryTest.groovy
+++ /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.util
-
-import spock.lang.Specification
-
-class DefaultClassLoaderFactoryTest extends Specification {
-    final DefaultClassLoaderFactory factory = new DefaultClassLoaderFactory()
-    ClassLoader original
-
-    def setup() {
-        original = Thread.currentThread().contextClassLoader
-    }
-
-    def cleanup() {
-        Thread.currentThread().contextClassLoader = original
-    }
-
-    def "classes from specified URLs are visible in isolated ClassLoader"() {
-        when:
-        def cl = factory.createIsolatedClassLoader(classpath)
-        def c = cl.loadClass(DefaultClassLoaderFactoryTestHelper.name)
-
-        then:
-        c.name == DefaultClassLoaderFactoryTestHelper.name
-        c != DefaultClassLoaderFactoryTestHelper
-    }
-
-    def "application classes are not visible in isolated ClassLoader"() {
-        when:
-        def cl = factory.createIsolatedClassLoader(classpath)
-        cl.loadClass(Closure.name)
-
-        then:
-        thrown(ClassNotFoundException)
-    }
-
-    def "can use XML APIs from isolated ClassLoader when application classes include an XML provider"() {
-        assert ClassLoader.getSystemResource("META-INF/services/javax.xml.parsers.SAXParserFactory")
-
-        when:
-        def cl = factory.createIsolatedClassLoader(classpath)
-        def c = cl.loadClass(DefaultClassLoaderFactoryTestHelper.name)
-
-        then:
-        c != DefaultClassLoaderFactoryTestHelper
-
-        when:
-        Thread.currentThread().contextClassLoader = cl
-        c.newInstance().doStuff()
-
-        then:
-        notThrown()
-    }
-
-    def "can use XML APIs from filtering ClassLoader when application classes include an XML provider"() {
-        assert ClassLoader.getSystemResource("META-INF/services/javax.xml.parsers.SAXParserFactory")
-
-        when:
-        def cl = new URLClassLoader(classpath.collect { it.toURL() } as URL[], factory.createFilteringClassLoader(getClass().classLoader))
-        def c = cl.loadClass(DefaultClassLoaderFactoryTestHelper.name)
-
-        then:
-        c != DefaultClassLoaderFactoryTestHelper
-
-        when:
-        Thread.currentThread().contextClassLoader = cl
-        c.newInstance().doStuff()
-
-        then:
-        notThrown()
-    }
-
-    def getClasspath() {
-        return [ClasspathUtil.getClasspathForClass(DefaultClassLoaderFactoryTestHelper)].collect { it.toURI() }
-    }
-}
-
diff --git a/subprojects/core/src/test/groovy/org/gradle/util/DefaultClassLoaderFactoryTestHelper.java b/subprojects/core/src/test/groovy/org/gradle/util/DefaultClassLoaderFactoryTestHelper.java
deleted file mode 100644
index 0d52d2b..0000000
--- a/subprojects/core/src/test/groovy/org/gradle/util/DefaultClassLoaderFactoryTestHelper.java
+++ /dev/null
@@ -1,35 +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.util;
-
-import javax.xml.XMLConstants;
-import javax.xml.datatype.DatatypeFactory;
-import javax.xml.parsers.DocumentBuilderFactory;
-import javax.xml.parsers.SAXParserFactory;
-import javax.xml.transform.TransformerFactory;
-import javax.xml.validation.SchemaFactory;
-import javax.xml.xpath.XPathFactory;
-
-public class DefaultClassLoaderFactoryTestHelper {
-    public void doStuff() throws Exception {
-        SAXParserFactory.newInstance().newSAXParser();
-        DocumentBuilderFactory.newInstance().newDocumentBuilder();
-        DatatypeFactory.newInstance().newXMLGregorianCalendar();
-        TransformerFactory.newInstance().newTransformer();
-        SchemaFactory.newInstance(XMLConstants.W3C_XML_SCHEMA_NS_URI);
-        XPathFactory.newInstance().newXPath();
-    }
-}
diff --git a/subprojects/core/src/test/groovy/org/gradle/util/FilteringClassLoaderTest.groovy b/subprojects/core/src/test/groovy/org/gradle/util/FilteringClassLoaderTest.groovy
deleted file mode 100644
index d4f778e..0000000
--- a/subprojects/core/src/test/groovy/org/gradle/util/FilteringClassLoaderTest.groovy
+++ /dev/null
@@ -1,182 +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.util
-
-import org.hamcrest.Matcher
-import org.jmock.integration.junit4.JMock
-import org.junit.Test
-import org.junit.runner.RunWith
-import org.junit.runners.BlockJUnit4ClassRunner
-import static org.hamcrest.Matchers.*
-import static org.junit.Assert.*
-import org.junit.Before
-
- at RunWith(JMock.class)
-class FilteringClassLoaderTest {
-    private final JUnit4GroovyMockery context = new JUnit4GroovyMockery()
-    private final FilteringClassLoader classLoader = new FilteringClassLoader(FilteringClassLoaderTest.class.getClassLoader())
-
-    @Test
-    void passesThroughSystemClasses() {
-        assertThat(classLoader.loadClass(String.class.name), sameInstance(String.class))
-    }
-
-    @Test
-    void passesThroughSystemPackages() {
-        assertThat(classLoader.getPackage('java.lang'), notNullValue(Package.class))
-        assertThat(classLoader.getPackages(), hasPackage('java.lang'))
-    }
-
-    private Matcher<Package[]> hasPackage(String name) {
-        Matcher matcher = [matches: {Package p -> p.name == name }, describeTo: {description -> description.appendText("has package '$name'")}] as Matcher
-        return hasItemInArray(matcher)
-    }
-
-    @Test
-    void passesThroughSystemResources() {
-        assertThat(classLoader.getResource('com/sun/jndi/ldap/jndiprovider.properties'), notNullValue())
-        assertThat(classLoader.getResourceAsStream('com/sun/jndi/ldap/jndiprovider.properties'), notNullValue())
-        assertTrue(classLoader.getResources('com/sun/jndi/ldap/jndiprovider.properties').hasMoreElements())
-    }
-
-    @Test
-    void filtersClasses() {
-        classLoader.parent.loadClass(Test.class.name)
-
-        try {
-            classLoader.loadClass(Test.class.name, false)
-            fail()
-        } catch (ClassNotFoundException e) {
-            assertThat(e.message, equalTo("$Test.name not found.".toString()))
-        }
-        try {
-            classLoader.loadClass(Test.class.name)
-            fail()
-        } catch (ClassNotFoundException e) {
-            assertThat(e.message, equalTo("$Test.name not found.".toString()))
-        }
-    }
-
-    @Test
-    void filtersPackages() {
-        assertThat(classLoader.parent.getPackage('org.junit'), notNullValue())
-
-        assertThat(classLoader.getPackage('org.junit'), nullValue())
-        assertThat(classLoader.getPackages(), not(hasPackage('org.junit')))
-    }
-
-    @Test
-    void filtersResources() {
-        assertThat(classLoader.parent.getResource('org/gradle/util/ClassLoaderTest.txt'), notNullValue())
-        assertThat(classLoader.getResource('org/gradle/util/ClassLoaderTest.txt'), nullValue())
-        assertThat(classLoader.getResourceAsStream('org/gradle/util/ClassLoaderTest.txt'), nullValue())
-        assertFalse(classLoader.getResources('org/gradle/util/ClassLoaderTest.txt').hasMoreElements())
-    }
-
-    @Test
-    void passesThroughClassesInSpecifiedPackages() {
-        classLoader.allowPackage('org.junit')
-        assertThat(classLoader.loadClass(Test.class.name), sameInstance(Test.class))
-        assertThat(classLoader.loadClass(Test.class.name, false), sameInstance(Test.class))
-        assertThat(classLoader.loadClass(BlockJUnit4ClassRunner.class.name), sameInstance(BlockJUnit4ClassRunner.class))
-    }
-
-    @Test
-    void passesThroughSpecifiedClasses() {
-        classLoader.allowClass(Test.class)
-        assertThat(classLoader.loadClass(Test.class.name), sameInstance(Test.class))
-        try {
-            classLoader.loadClass(Before.class.name)
-            fail()
-        } catch (ClassNotFoundException e) {
-            // expected
-        }
-    }
-
-    @Test
-    void filtersSpecifiedClasses() {
-        classLoader.allowPackage("org.junit")
-        classLoader.disallowClass("org.junit.Test")
-
-        canLoadClass(Before)
-        cannotLoadClass(Test)
-    }
-
-    @Test
-    void disallowClassWinsOverAllowClass() {
-        classLoader.allowClass(Test)
-        classLoader.disallowClass(Test.name)
-
-        cannotLoadClass(Test)
-    }
-
-    @Test
-    void passesThroughSpecifiedPackages() {
-        assertThat(classLoader.getPackage('org.junit'), nullValue())
-        assertThat(classLoader.getPackages(), not(hasPackage('org.junit')))
-
-        classLoader.allowPackage('org.junit')
-
-        assertThat(classLoader.getPackage('org.junit'), notNullValue())
-        assertThat(classLoader.getPackages(), hasPackage('org.junit'))
-        assertThat(classLoader.getPackage('org.junit.runner'), notNullValue())
-        assertThat(classLoader.getPackages(), hasPackage('org.junit.runner'))
-    }
-
-    @Test
-    void passesThroughResourcesInSpecifiedPackages() {
-        assertThat(classLoader.getResource('org/gradle/util/ClassLoaderTest.txt'), nullValue())
-
-        classLoader.allowPackage('org.gradle')
-
-        assertThat(classLoader.getResource('org/gradle/util/ClassLoaderTest.txt'), notNullValue())
-        assertThat(classLoader.getResourceAsStream('org/gradle/util/ClassLoaderTest.txt'), notNullValue())
-        assertTrue(classLoader.getResources('org/gradle/util/ClassLoaderTest.txt').hasMoreElements())
-    }
-
-    @Test
-    void passesThroughResourcesWithSpecifiedPrefix() {
-        assertThat(classLoader.getResource('org/gradle/util/ClassLoaderTest.txt'), nullValue())
-
-        classLoader.allowResources('org/gradle')
-
-        assertThat(classLoader.getResource('org/gradle/util/ClassLoaderTest.txt'), notNullValue())
-        assertThat(classLoader.getResourceAsStream('org/gradle/util/ClassLoaderTest.txt'), notNullValue())
-        assertTrue(classLoader.getResources('org/gradle/util/ClassLoaderTest.txt').hasMoreElements())
-    }
-
-    @Test
-    void passesThroughSpecifiedResources() {
-        assertThat(classLoader.getResource('org/gradle/util/ClassLoaderTest.txt'), nullValue())
-
-        classLoader.allowResource('org/gradle/util/ClassLoaderTest.txt')
-
-        assertThat(classLoader.getResource('org/gradle/util/ClassLoaderTest.txt'), notNullValue())
-        assertThat(classLoader.getResourceAsStream('org/gradle/util/ClassLoaderTest.txt'), notNullValue())
-        assertTrue(classLoader.getResources('org/gradle/util/ClassLoaderTest.txt').hasMoreElements())
-    }
-
-    void canLoadClass(Class<?> clazz) {
-        assert classLoader.loadClass(clazz.name) == clazz
-    }
-
-    void cannotLoadClass(Class<?> clazz) {
-        try {
-            classLoader.loadClass(clazz.name)
-            fail()
-        } catch (ClassNotFoundException expected) {}
-    }
-}
diff --git a/subprojects/core/src/test/groovy/org/gradle/util/GFileUtilsTest.groovy b/subprojects/core/src/test/groovy/org/gradle/util/GFileUtilsTest.groovy
index e241b24..03a221c 100644
--- a/subprojects/core/src/test/groovy/org/gradle/util/GFileUtilsTest.groovy
+++ b/subprojects/core/src/test/groovy/org/gradle/util/GFileUtilsTest.groovy
@@ -24,9 +24,6 @@ import spock.lang.Specification
 import static org.gradle.util.GFileUtils.mkdirs
 import static org.gradle.util.GFileUtils.parentMkdirs
 
-/**
- * by Szczepan Faber, created at: 2/28/12
- */
 class GFileUtilsTest extends Specification {
 
     @Rule TestNameTestDirectoryProvider temp
diff --git a/subprojects/core/src/test/groovy/org/gradle/util/GStreamUtilTest.groovy b/subprojects/core/src/test/groovy/org/gradle/util/GStreamUtilTest.groovy
new file mode 100644
index 0000000..93cef4d
--- /dev/null
+++ b/subprojects/core/src/test/groovy/org/gradle/util/GStreamUtilTest.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.util
+
+import spock.lang.Specification
+
+class GStreamUtilTest extends Specification {
+
+    def "skips bytes"() {
+        def bytes = new ByteArrayOutputStream()
+        bytes.write(5)
+        bytes.write(10)
+        bytes.write(15)
+        bytes.write(20)
+
+        when:
+        def is = new DataInputStream(new ByteArrayInputStream(bytes.toByteArray()))
+        def skipped = GStreamUtil.skipBytes(2, is)
+
+        then:
+        skipped == 2
+        is.read() == 15
+        GStreamUtil.skipBytes(123, is) == 1
+    }
+}
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 c0de292..21b25af 100644
--- a/subprojects/core/src/test/groovy/org/gradle/util/GradleVersionTest.groovy
+++ b/subprojects/core/src/test/groovy/org/gradle/util/GradleVersionTest.groovy
@@ -24,22 +24,33 @@ import org.gradle.internal.os.OperatingSystem
 import spock.lang.Issue
 import spock.lang.Specification
 
-/**
- * @author Hans Dockter
- */
 class GradleVersionTest extends Specification {
     final GradleVersion version = GradleVersion.current()
 
-    def "valid versions"() {
-        expect:
-        version.valid
-        !GradleVersion.version("asdfasdfas").valid
-        GradleVersion.version("1.0").valid
+    def "parsing fails for unrecognized version string"() {
+        when:
+        GradleVersion.version(versionString)
+
+        then:
+        IllegalArgumentException e = thrown()
+        e.message == "'$versionString' is not a valid Gradle version string (examples: '1.0', '1.0-rc-1')"
+
+        where:
+        versionString << [
+                "",
+                "something",
+                "1",
+                "1-beta",
+                "1.0-\n"
+        ]
     }
 
-    def currentVersionHasNonNullVersion() {
+    def "current version has non-null parts"() {
         expect:
         version.version
+        version.buildTime
+        version.nextMajor
+        version.baseVersion
     }
 
     @Issue("http://issues.gradle.org/browse/GRADLE-1892")
@@ -96,28 +107,6 @@ class GradleVersionTest extends Specification {
                 '1.2.1']
     }
 
-    def canOnlyQueryVersionStringForUnrecognizedVersion(String version) {
-        def gradleVersion = GradleVersion.version(version)
-
-        expect:
-        gradleVersion.version == version
-        gradleVersion.snapshot
-
-        when:
-        gradleVersion > GradleVersion.version('1.2')
-
-        then:
-        IllegalArgumentException e = thrown()
-        e.message == "Cannot compare unrecognized Gradle version '${version}'."
-
-        where:
-        version << [
-                'abc',
-                '3.0-status-5-master',
-                'user-master',
-        ]
-    }
-
     def canCompareMajorVersions() {
         expect:
         GradleVersion.version(a) > GradleVersion.version(b)
@@ -173,8 +162,9 @@ class GradleVersionTest extends Specification {
         a                 | b
         '1.0-milestone-2' | '1.0-milestone-1'
         '1.0-preview-2'   | '1.0-preview-1'
-        '1.0-rc-2'        | '1.0-rc-1'
         '1.0-preview-1'   | '1.0-milestone-7'
+        '1.0-rc-1'        | '1.0-milestone-7'
+        '1.0-rc-2'        | '1.0-rc-1'
         '1.0-rc-7'        | '1.0-rc-1'
         '1.0'             | '1.0-rc-7'
     }
@@ -215,28 +205,53 @@ class GradleVersionTest extends Specification {
 
     def "can get version base"() {
         expect:
-        GradleVersion.version(v).versionBase == base
+        GradleVersion.version(v).baseVersion == GradleVersion.version(base)
 
         where:
-        v                         | base
-        "1.0"                     | "1.0"
-        "1.0-rc-1"                | "1.0"
-        '0.9-20101220100000+1000' | "0.9"
-        '0.9-20101220100000'      | "0.9"
-        "asdfasd"                 | null
+        v                                     | base
+        "1.0"                                 | "1.0"
+        "1.0-rc-1"                            | "1.0"
+        "1.2.3.4"                             | "1.2.3.4"
+        '0.9'                                 | "0.9"
+        '0.9.2'                               | "0.9.2"
+        '0.9-20101220100000+1000'             | "0.9"
+        '0.9-20101220100000'                  | "0.9"
+        '20.17-20101220100000+1000'           | "20.17"
     }
 
-    def "can get version major"() {
+    def "milestones are treated as base versions"() {
         expect:
-        GradleVersion.version(v).major == major
+        GradleVersion.version(v).baseVersion == GradleVersion.version(base)
 
         where:
-        v                         | major
-        "1.0"                     | 1
-        "1.0-rc-1"                | 1
-        '0.9-20101220100000+1000' | 0
-        '0.9-20101220100000'      | 0
-        "asdfasd"                 | -1
+        v                                     | base
+        '1.0-milestone-3'                     | "1.0-milestone-3"
+        '1.0-milestone-3-20121012100000+1000' | "1.0-milestone-3"
+        '2.0-milestone-3'                     | "2.0-milestone-3"
+    }
+
+    def "can get next major version"() {
+        expect:
+        GradleVersion.version(v).nextMajor == GradleVersion.version(major)
+
+        where:
+        v                                     | major
+        "1.0"                                 | "2.0"
+        "1.0-rc-1"                            | "2.0"
+        '0.9-20101220100000+1000'             | "1.0"
+        '0.9-20101220100000'                  | "1.0"
+        '20.17-20101220100000+1000'           | "21.0"
+    }
+
+    def "milestones are part of previous major version"() {
+        expect:
+        GradleVersion.version(v).nextMajor == GradleVersion.version(major)
+
+        where:
+        v                                     | major
+        '1.0-milestone-3'                     | "1.0"
+        '1.0-milestone-3-20121012100000+1000' | "1.0"
+        '2.0-milestone-3'                     | "2.0" // not that we're planning to do this
     }
 
     def prettyPrint() {
@@ -245,12 +260,15 @@ class GradleVersionTest extends Specification {
 Gradle $version.version
 ------------------------------------------------------------
 
-Gradle build time: $version.buildTime
-Groovy: $InvokerHelper.version
-Ant: $Main.antVersion
-Ivy: ${Ivy.ivyVersion}
-JVM: ${Jvm.current()}
-OS: ${OperatingSystem.current()}
+Build time:   $version.buildTime
+Build number: $version.buildNumber
+Revision:     $version.commitId
+
+Groovy:       $InvokerHelper.version
+Ant:          $Main.antVersion
+Ivy:          ${Ivy.ivyVersion}
+JVM:          ${Jvm.current()}
+OS:           ${OperatingSystem.current()}
 """
         expect:
         version.prettyPrint() == expectedText
diff --git a/subprojects/core/src/test/groovy/org/gradle/util/JavaMethodTest.java b/subprojects/core/src/test/groovy/org/gradle/util/JavaMethodTest.java
deleted file mode 100644
index 850778b..0000000
--- a/subprojects/core/src/test/groovy/org/gradle/util/JavaMethodTest.java
+++ /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.util;
-
-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 static org.hamcrest.Matchers.*;
-import static org.junit.Assert.*;
-
- at RunWith(JMock.class)
-public class JavaMethodTest {
-    private final JUnit4Mockery context = new JUnit4Mockery();
-
-    @Test
-    public void invokesMethodOnObject() {
-        JavaMethod<CharSequence, CharSequence> method = JavaMethod.create(CharSequence.class, CharSequence.class, "subSequence", int.class, int.class);
-        assertThat(method.invoke("string", 0, 3), equalTo((CharSequence) "str"));
-    }
-    
-    @Test
-    public void propagatesExceptionThrownByMethod() {
-        final CharSequence mock = context.mock(CharSequence.class);
-        final RuntimeException failure = new RuntimeException();
-        context.checking(new Expectations() {{
-            one(mock).subSequence(0, 3);
-            will(throwException(failure));
-        }});
-
-        JavaMethod<CharSequence, CharSequence> method = JavaMethod.create(CharSequence.class, CharSequence.class, "subSequence", int.class, int.class);
-        try {
-            method.invoke(mock, 0, 3);
-            fail();
-        } catch (RuntimeException e) {
-            assertThat(e, sameInstance(failure));
-        }
-    }
-
-    @Test
-    public void canAccessProtectedMethod() {
-        final Package[] packages = new Package[0];
-        ClassLoader classLoader = new ClassLoader() {
-            @Override
-            protected Package[] getPackages() {
-                return packages;
-            }
-        };
-
-        JavaMethod<ClassLoader, Package[]> method = JavaMethod.create(ClassLoader.class, Package[].class, "getPackages");
-        assertThat(method.invoke(classLoader), sameInstance(packages));
-    }
-}
diff --git a/subprojects/core/src/test/groovy/org/gradle/util/MatchersTest.groovy b/subprojects/core/src/test/groovy/org/gradle/util/MatchersTest.groovy
index 7c7dbbe..3cb9951 100644
--- a/subprojects/core/src/test/groovy/org/gradle/util/MatchersTest.groovy
+++ b/subprojects/core/src/test/groovy/org/gradle/util/MatchersTest.groovy
@@ -22,9 +22,6 @@ import java.util.regex.Pattern
 
 import static org.gradle.util.Matchers.matchesRegexp
 
-/**
- * by Szczepan Faber, created at: 12/17/12
- */
 class MatchersTest extends Specification {
 
     def "matches regex"() {
diff --git a/subprojects/core/src/test/groovy/org/gradle/util/MultiParentClassLoaderTest.groovy b/subprojects/core/src/test/groovy/org/gradle/util/MultiParentClassLoaderTest.groovy
deleted file mode 100644
index 6efd696..0000000
--- a/subprojects/core/src/test/groovy/org/gradle/util/MultiParentClassLoaderTest.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.util
-
-import org.jmock.integration.junit4.JMock
-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)
-class MultiParentClassLoaderTest {
-    private final JUnit4GroovyMockery context = new JUnit4GroovyMockery()
-    private ClassLoader parent1
-    private ClassLoader parent2
-    private MultiParentClassLoader loader
-
-    @Before
-    public void setup() {
-        parent1 = context.mock(ClassLoader)
-        parent2 = context.mock(ClassLoader)
-        loader = new MultiParentClassLoader(parent1, parent2)
-    }
-
-    @Test
-    public void parentsAreNotVisibleViaSuperClass() {
-        assertThat(loader.parent, nullValue())
-    }
-
-    @Test
-    public void loadsClassFromParentsInOrderSpecified() {
-        Class stringClass = String.class
-        Class integerClass = Integer.class
-
-        context.checking {
-            allowing(parent1).loadClass('string')
-            will(returnValue(stringClass))
-            allowing(parent1).loadClass('integer')
-            will(throwException(new ClassNotFoundException()))
-            allowing(parent2).loadClass('integer')
-            will(returnValue(integerClass))
-        }
-
-        assertThat(loader.loadClass('string'), equalTo(String.class))
-        assertThat(loader.loadClass('string', true), equalTo(String.class))
-        assertThat(loader.loadClass('integer'), equalTo(Integer.class))
-        assertThat(loader.loadClass('integer', true), equalTo(Integer.class))
-    }
-
-    @Test
-    public void throwsCNFExceptionWhenClassNotFound() {
-        context.checking {
-            allowing(parent1).loadClass('string')
-            will(throwException(new ClassNotFoundException()))
-            allowing(parent2).loadClass('string')
-            will(throwException(new ClassNotFoundException()))
-        }
-
-        try {
-            loader.loadClass('string')
-            fail()
-        } catch (ClassNotFoundException e) {
-            assertThat(e.message, equalTo('string not found.'))
-        }
-    }
-    
-    @Test
-    public void loadsPackageFromParentsInOrderSpecified() {
-        Package stringPackage = String.class.getPackage()
-        Package listPackage = List.class.getPackage()
-
-        context.checking {
-            allowing(parent1).getPackage('string')
-            will(returnValue(stringPackage))
-            allowing(parent1).getPackage('list')
-            will(returnValue(null))
-            allowing(parent2).getPackage('list')
-            will(returnValue(listPackage))
-        }
-
-        assertThat(loader.getPackage('string'), sameInstance(stringPackage))
-        assertThat(loader.getPackage('list'), sameInstance(listPackage))
-    }
-
-    @Test
-    public void containsUnionOfPackagesFromAllParents() {
-        Package package1 = context.mock(Package.class, 'p1')
-        Package package2 = context.mock(Package.class, 'p2')
-
-        context.checking {
-            allowing(parent1).getPackages()
-            will(returnValue([package1] as Package[]))
-            allowing(parent2).getPackages()
-            will(returnValue([package2] as Package[]))
-        }
-
-        assertThat(loader.getPackages(), hasItemInArray(package1))
-        assertThat(loader.getPackages(), hasItemInArray(package2))
-    }
-
-    @Test
-    public void loadsResourceFromParentsInOrderSpecified() {
-        URL resource1 = new File('res1').toURI().toURL()
-        URL resource2 = new File('res2').toURI().toURL()
-
-        context.checking {
-            allowing(parent1).getResource('resource1')
-            will(returnValue(resource1))
-            allowing(parent1).getResource('resource2')
-            will(returnValue(null))
-            allowing(parent2).getResource('resource2')
-            will(returnValue(resource2))
-        }
-
-        assertThat(loader.getResource('resource1'), equalTo(resource1))
-        assertThat(loader.getResource('resource2'), equalTo(resource2))
-    }
-    
-    @Test
-    public void containsUnionOfResourcesFromAllParents() {
-        URL resource1 = new File('res1').toURI().toURL()
-        URL resource2 = new File('res2').toURI().toURL()
-
-        context.checking {
-            allowing(parent1).getResources('resource1')
-            will(returnValue(Collections.enumeration([resource1])))
-            allowing(parent2).getResources('resource1')
-            will(returnValue(Collections.enumeration([resource2, resource1])))
-        }
-
-        Enumeration resources = loader.getResources('resource1')
-        assertTrue(resources.hasMoreElements())
-        assertThat(resources.nextElement(), sameInstance(resource1))
-        assertTrue(resources.hasMoreElements())
-        assertThat(resources.nextElement(), sameInstance(resource2))
-        assertFalse(resources.hasMoreElements())
-    }
-}
diff --git a/subprojects/core/src/test/groovy/org/gradle/util/PathTest.groovy b/subprojects/core/src/test/groovy/org/gradle/util/PathTest.groovy
index 23c5052..5928d69 100644
--- a/subprojects/core/src/test/groovy/org/gradle/util/PathTest.groovy
+++ b/subprojects/core/src/test/groovy/org/gradle/util/PathTest.groovy
@@ -17,11 +17,9 @@
 package org.gradle.util
 
 import spock.lang.Specification
-import static org.gradle.util.Matchers.*
 
-/**
- * @author Hans Dockter
- */
+import static org.gradle.util.Matchers.strictlyEquals
+
 class PathTest extends Specification {
     def construction() {
         expect:
diff --git a/subprojects/core/src/test/groovy/org/gradle/util/SingleMessageLoggerTest.groovy b/subprojects/core/src/test/groovy/org/gradle/util/SingleMessageLoggerTest.groovy
index c8b198e..81da2f9 100644
--- a/subprojects/core/src/test/groovy/org/gradle/util/SingleMessageLoggerTest.groovy
+++ b/subprojects/core/src/test/groovy/org/gradle/util/SingleMessageLoggerTest.groovy
@@ -19,24 +19,31 @@ package org.gradle.util
 import org.gradle.internal.Factory
 import org.gradle.logging.ConfigureLogging
 import org.gradle.logging.TestAppender
+import org.gradle.test.fixtures.concurrent.ConcurrentSpec
 import org.junit.Rule
-import spock.lang.Specification
 
-class SingleMessageLoggerTest extends Specification {
+class SingleMessageLoggerTest extends ConcurrentSpec {
     final TestAppender appender = new TestAppender()
     @Rule final ConfigureLogging logging = new ConfigureLogging(appender)
 
-    public void cleanup() {
+    def cleanup() {
         SingleMessageLogger.reset()
     }
 
-    def "logs deprecation warning once"() {
+    def "logs deprecation warning once until reset"() {
         when:
         SingleMessageLogger.nagUserWith("nag")
         SingleMessageLogger.nagUserWith("nag")
 
         then:
         appender.toString() == '[WARN nag]'
+
+        when:
+        SingleMessageLogger.reset()
+        SingleMessageLogger.nagUserWith("nag")
+
+        then:
+        appender.toString() == '[WARN nag][WARN nag]'
     }
 
     def "does not log warning while disabled with factory"() {
@@ -54,6 +61,9 @@ class SingleMessageLoggerTest extends Specification {
             return "result"
         }
         0 * _._
+
+        and:
+        appender.toString().length() == 0
     }
 
     def "does not log warning while disabled with action"() {
@@ -65,19 +75,40 @@ class SingleMessageLoggerTest extends Specification {
         then:
         1 * action.run()
         0 * _._
+
+        and:
+        appender.toString().length() == 0
+    }
+
+    def "warnings are disabled for the current thread only"() {
+        when:
+        async {
+            start {
+                thread.blockUntil.disabled
+                SingleMessageLogger.nagUserWith("nag")
+                instant.logged
+            }
+            start {
+                SingleMessageLogger.whileDisabled {
+                    instant.disabled
+                    SingleMessageLogger.nagUserWith("ignored")
+                    thread.blockUntil.logged
+                }
+            }
+        }
+
+        then:
+        appender.toString() == '[WARN nag]'
     }
 
     def "deprecation message has next major version"() {
         given:
-        def major = GradleVersion.current().major
-
-        expect:
-        major != -1
+        def major = GradleVersion.current().nextMajor
 
         when:
         SingleMessageLogger.nagUserOfDeprecated("foo", "bar")
 
         then:
-        appender.toString() == "[WARN foo has been deprecated and is scheduled to be removed in Gradle ${major + 1}.0. bar.]"
+        appender.toString() == "[WARN foo has been deprecated and is scheduled to be removed in Gradle ${major.version}. bar.]"
     }
 }
diff --git a/subprojects/core/src/test/groovy/org/gradle/util/StageTest.groovy b/subprojects/core/src/test/groovy/org/gradle/util/StageTest.groovy
index e501cdb..ac5227f 100644
--- a/subprojects/core/src/test/groovy/org/gradle/util/StageTest.groovy
+++ b/subprojects/core/src/test/groovy/org/gradle/util/StageTest.groovy
@@ -19,9 +19,6 @@ 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:
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 ac097c6..dffd28e 100644
--- a/subprojects/core/src/test/groovy/org/gradle/util/TextUtilTest.groovy
+++ b/subprojects/core/src/test/groovy/org/gradle/util/TextUtilTest.groovy
@@ -29,6 +29,8 @@ class TextUtilTest extends Specification {
 
         where:
         original                          | converted
+        ""                                | ""
+        "none"                            | "none"
         "one\rtwo\nthree\r\nfour\n\rfive" | "one${sep}two${sep}three${sep}four${sep}${sep}five"
     }
 
@@ -38,10 +40,24 @@ class TextUtilTest extends Specification {
 
         where:
         original                          | converted
+        ""                                | ""
+        "none"                            | "none"
         "one\rtwo\nthree\r\nfour\n\rfive" | "one${platformSep}two${platformSep}three${platformSep}four${platformSep}${platformSep}five"
         "\n\n"                            | "${platformSep}${platformSep}"
     }
 
+    def normaliseLineSeparators() {
+        expect:
+        TextUtil.normaliseLineSeparators(original) == converted
+
+        where:
+        original                          | converted
+        ""                                | ""
+        "none"                            | "none"
+        "one\rtwo\nthree\r\nfour\n\rfive" | "one\ntwo\nthree\nfour\n\nfive"
+        "\r\n\n\r"                        | "\n\n\n"
+    }
+
     def containsWhitespace() {
         expect:
         TextUtil.containsWhitespace(str) == whitespace
@@ -62,11 +78,11 @@ class TextUtilTest extends Specification {
         TextUtil.indent(text, indent) == result
 
         where:
-        text            | indent | result
-        ""              | ""     | ""
-        "abc"           | "  "   | "  abc"
-        "abc"           | "def"  | "defabc"
-        "abc\ndef\nghi" | " "    | " abc\n def\n ghi"
+        text                | indent | result
+        ""                  | ""     | ""
+        "abc"               | "  "   | "  abc"
+        "abc"               | "def"  | "defabc"
+        "abc\ndef\nghi"     | " "    | " abc\n def\n ghi"
         "abc\n\t\n   \nghi" | "X"    | "Xabc\n\t\n   \nXghi"
     }
 }
diff --git a/subprojects/core/src/test/groovy/org/gradle/util/VersionNumberTest.groovy b/subprojects/core/src/test/groovy/org/gradle/util/VersionNumberTest.groovy
index 96bc172..3287063 100644
--- a/subprojects/core/src/test/groovy/org/gradle/util/VersionNumberTest.groovy
+++ b/subprojects/core/src/test/groovy/org/gradle/util/VersionNumberTest.groovy
@@ -73,7 +73,7 @@ class VersionNumberTest extends Specification {
         new VersionNumber(2, 1, 1, null) != new VersionNumber(1, 1, 1, null)
         new VersionNumber(1, 2, 1, null) != new VersionNumber(1, 1, 1, null)
         new VersionNumber(1, 1, 2, null) != new VersionNumber(1, 1, 1, null)
-        new VersionNumber(1, 1, 1, "foo") != new VersionNumber(1, 1, 1, null)
+        new VersionNumber(1, 1, 1, "rc") != new VersionNumber(1, 1, 1, null)
     }
 
     def "comparison"() {
@@ -83,14 +83,22 @@ class VersionNumberTest extends Specification {
         new VersionNumber(2, 1, 1, null) > new VersionNumber(1, 1, 1, null)
         new VersionNumber(1, 2, 1, null) > new VersionNumber(1, 1, 1, null)
         new VersionNumber(1, 1, 2, null) > new VersionNumber(1, 1, 1, null)
-        new VersionNumber(1, 1, 1, "foo") > new VersionNumber(1, 1, 1, null)
-        new VersionNumber(1, 1, 1, "b") > new VersionNumber(1, 1, 1, "a")
+        new VersionNumber(1, 1, 1, "rc") < new VersionNumber(1, 1, 1, null)
+        new VersionNumber(1, 1, 1, "beta") > new VersionNumber(1, 1, 1, "alpha")
+        new VersionNumber(1, 1, 1, "RELEASE") > new VersionNumber(1, 1, 1, "beta")
 
         new VersionNumber(1, 1, 1, null) < new VersionNumber(2, 1, 1, null)
         new VersionNumber(1, 1, 1, null) < new VersionNumber(1, 2, 1, null)
         new VersionNumber(1, 1, 1, null) < new VersionNumber(1, 1, 2, null)
-        new VersionNumber(1, 1, 1, null) < new VersionNumber(1, 1, 1, "foo")
-        new VersionNumber(1, 1, 1, "a") < new VersionNumber(1, 1, 1, "b")
+        new VersionNumber(1, 1, 1, null) > new VersionNumber(1, 1, 1, "rc")
+        new VersionNumber(1, 1, 1, "alpha") < new VersionNumber(1, 1, 1, "beta")
+        new VersionNumber(1, 1, 1, "beta") < new VersionNumber(1, 1, 1, "RELEASE")
+    }
+
+    def "base version"() {
+        expect:
+        new VersionNumber(1, 2, 3, null).baseVersion == new VersionNumber(1, 2, 3, null)
+        new VersionNumber(1, 2, 3, "beta").baseVersion == new VersionNumber(1, 2, 3, null)
     }
 }
 
diff --git a/subprojects/core/src/test/groovy/org/gradle/util/internal/ArgumentsSplitterTest.groovy b/subprojects/core/src/test/groovy/org/gradle/util/internal/ArgumentsSplitterTest.groovy
index 4d88fd7..aab8018 100644
--- a/subprojects/core/src/test/groovy/org/gradle/util/internal/ArgumentsSplitterTest.groovy
+++ b/subprojects/core/src/test/groovy/org/gradle/util/internal/ArgumentsSplitterTest.groovy
@@ -17,11 +17,9 @@
 package org.gradle.util.internal
 
 import spock.lang.Specification
+
 import static org.gradle.util.internal.ArgumentsSplitter.split
 
-/**
- * by Szczepan Faber, created at: 2/29/12
- */
 class ArgumentsSplitterTest extends Specification {
 
     def breaksUpEmptyCommandLineIntoEmptyList() {
diff --git a/subprojects/core/src/test/groovy/org/gradle/util/internal/LimitedDescriptionTest.groovy b/subprojects/core/src/test/groovy/org/gradle/util/internal/LimitedDescriptionTest.groovy
index e31a6ce..c37064d 100644
--- a/subprojects/core/src/test/groovy/org/gradle/util/internal/LimitedDescriptionTest.groovy
+++ b/subprojects/core/src/test/groovy/org/gradle/util/internal/LimitedDescriptionTest.groovy
@@ -18,9 +18,6 @@ package org.gradle.util.internal
 
 import spock.lang.Specification
 
-/**
- * by Szczepan Faber, created at: 2/28/12
- */
 class LimitedDescriptionTest extends Specification {
 
     def desc = new LimitedDescription(2)
diff --git a/subprojects/core/src/testFixtures/groovy/org/gradle/api/internal/file/TestFiles.java b/subprojects/core/src/testFixtures/groovy/org/gradle/api/internal/file/TestFiles.java
index 7517678..052b3b5 100644
--- a/subprojects/core/src/testFixtures/groovy/org/gradle/api/internal/file/TestFiles.java
+++ b/subprojects/core/src/testFixtures/groovy/org/gradle/api/internal/file/TestFiles.java
@@ -15,7 +15,8 @@
  */
 package org.gradle.api.internal.file;
 
-import org.gradle.internal.nativeplatform.filesystem.FileSystems;
+import org.gradle.internal.nativeplatform.filesystem.FileSystem;
+import org.gradle.internal.nativeplatform.services.NativeServices;
 
 import java.io.File;
 
@@ -24,13 +25,13 @@ public class TestFiles {
      * Returns a resolver with no base directory.
      */
     public static FileResolver resolver() {
-        return new IdentityFileResolver();
+        return new IdentityFileResolver(NativeServices.getInstance().get(FileSystem.class));
     }
 
     /**
      * Returns a resolver with the given base directory.
      */
     public static FileResolver resolver(File baseDir) {
-        return new BaseDirFileResolver(FileSystems.getDefault(), baseDir);
+        return resolver().withBaseDir(baseDir);
     }
 }
diff --git a/subprojects/core/src/testFixtures/groovy/org/gradle/api/internal/file/copy/CopyActionExecuterUtil.java b/subprojects/core/src/testFixtures/groovy/org/gradle/api/internal/file/copy/CopyActionExecuterUtil.java
new file mode 100644
index 0000000..2bcc283
--- /dev/null
+++ b/subprojects/core/src/testFixtures/groovy/org/gradle/api/internal/file/copy/CopyActionExecuterUtil.java
@@ -0,0 +1,40 @@
+/*
+ * Copyright 2013 the original author or authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY 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.internal.file.CopyActionProcessingStreamAction;
+import org.gradle.api.tasks.WorkResult;
+
+import java.util.Arrays;
+
+public class CopyActionExecuterUtil {
+
+    public static WorkResult visit(CopyAction visitor, final Iterable<FileCopyDetailsInternal> detailses) {
+        return visitor.execute(new CopyActionProcessingStream() {
+            public void process(CopyActionProcessingStreamAction action) {
+                for (FileCopyDetailsInternal detailsInternal : detailses) {
+                    action.processFile(detailsInternal);
+                }
+            }
+        });
+    }
+
+    public static WorkResult visit(CopyAction visitor, final FileCopyDetailsInternal... detailses) {
+        return visit(visitor, Arrays.asList(detailses));
+    }
+
+}
diff --git a/subprojects/core/src/testFixtures/groovy/org/gradle/api/tasks/AbstractConventionTaskTest.java b/subprojects/core/src/testFixtures/groovy/org/gradle/api/tasks/AbstractConventionTaskTest.java
index 3172e06..a2bf3d5 100644
--- a/subprojects/core/src/testFixtures/groovy/org/gradle/api/tasks/AbstractConventionTaskTest.java
+++ b/subprojects/core/src/testFixtures/groovy/org/gradle/api/tasks/AbstractConventionTaskTest.java
@@ -13,9 +13,10 @@
  * 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.ConventionAwareHelper;
 import org.gradle.api.internal.ConventionTask;
 import org.junit.Test;
@@ -24,16 +25,13 @@ import static org.hamcrest.Matchers.instanceOf;
 import static org.hamcrest.Matchers.sameInstance;
 import static org.junit.Assert.assertThat;
 
-/**
- * @author Hans Dockter
- */
 public abstract class AbstractConventionTaskTest extends AbstractTaskTest {
 
-    public abstract ConventionTask getTask();
-    
+    public abstract AbstractTask getTask();
+
     @Test
     public void testConventionAwareness() {
-        ConventionTask task = getTask();
+        ConventionTask task = (ConventionTask) getTask();
         assertThat(task.getConventionMapping(), instanceOf(ConventionAwareHelper.class));
         ConventionAwareHelper conventionMapping = (ConventionAwareHelper) task.getConventionMapping();
         assertThat(conventionMapping.getConvention(), sameInstance(getProject().getConvention()));
diff --git a/subprojects/core/src/testFixtures/groovy/org/gradle/api/tasks/AbstractCopyTaskContractTest.groovy b/subprojects/core/src/testFixtures/groovy/org/gradle/api/tasks/AbstractCopyTaskContractTest.groovy
new file mode 100644
index 0000000..e95cbf5
--- /dev/null
+++ b/subprojects/core/src/testFixtures/groovy/org/gradle/api/tasks/AbstractCopyTaskContractTest.groovy
@@ -0,0 +1,44 @@
+/*
+ * Copyright 2013 the original author or authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF 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.Action
+import org.gradle.api.file.FileCopyDetails
+import org.gradle.api.internal.DynamicObject
+import org.junit.Test
+import spock.lang.Unroll
+
+/**
+ * Tests that different types of copy tasks correctly expose DSL enhanced objects.
+ */
+abstract class AbstractCopyTaskContractTest extends AbstractConventionTaskTest {
+
+    abstract AbstractCopyTask getTask()
+
+    @Unroll
+    @Test
+    public void rootLevelFileCopyDetailsIsDslEnhanced() {
+        task.eachFile {
+            assert delegate instanceof DynamicObject
+        }
+        task.eachFile(new Action<FileCopyDetails>() {
+            void execute(FileCopyDetails fcd) {
+                assert fcd instanceof DynamicObject
+            }
+        })
+    }
+}
diff --git a/subprojects/core/src/testFixtures/groovy/org/gradle/api/tasks/AbstractSpockTaskTest.groovy b/subprojects/core/src/testFixtures/groovy/org/gradle/api/tasks/AbstractSpockTaskTest.groovy
index d6f57ef..94d2805 100644
--- a/subprojects/core/src/testFixtures/groovy/org/gradle/api/tasks/AbstractSpockTaskTest.groovy
+++ b/subprojects/core/src/testFixtures/groovy/org/gradle/api/tasks/AbstractSpockTaskTest.groovy
@@ -29,13 +29,14 @@ import org.gradle.api.internal.project.taskfactory.AnnotationProcessingTaskFacto
 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.TaskExecutionContext
 import org.gradle.api.internal.tasks.TaskStateInternal
 import org.gradle.api.specs.Spec
 import org.gradle.internal.reflect.DirectInstantiator
 import org.gradle.test.fixtures.file.TestNameTestDirectoryProvider
 import org.gradle.util.GUtil
-import org.gradle.util.HelperUtil
 import org.gradle.util.Matchers
+import org.gradle.util.TestUtil
 import org.junit.Rule
 import spock.lang.Specification
 
@@ -43,15 +44,12 @@ import java.util.concurrent.atomic.AtomicBoolean
 
 import static org.junit.Assert.assertFalse
 
-/**
- * @author Hans Dockter
- */
 public abstract class AbstractSpockTaskTest extends Specification {
     public static final String TEST_TASK_NAME = "taskname"
     @Rule
     public TestNameTestDirectoryProvider tmpDir = new TestNameTestDirectoryProvider()
 
-    private AbstractProject project = HelperUtil.createRootProject()
+    private AbstractProject project = TestUtil.createRootProject()
 
     private static final ITaskFactory TASK_FACTORY = new AnnotationProcessingTaskFactory(new TaskFactory(new AsmBackedClassGenerator()))
 
@@ -87,10 +85,10 @@ public abstract class AbstractSpockTaskTest extends Specification {
     }
 
     def testPath() {
-        DefaultProject rootProject = HelperUtil.createRootProject();
-        DefaultProject childProject = HelperUtil.createChildProject(rootProject, "child");
+        DefaultProject rootProject = TestUtil.createRootProject();
+        DefaultProject childProject = TestUtil.createChildProject(rootProject, "child");
         childProject.getProjectDir().mkdirs();
-        DefaultProject childchildProject = HelperUtil.createChildProject(childProject, "childchild");
+        DefaultProject childchildProject = TestUtil.createChildProject(childProject, "childchild");
         childchildProject.getProjectDir().mkdirs();
 
         when:
@@ -164,7 +162,7 @@ public abstract class AbstractSpockTaskTest extends Specification {
         task.execute()
 
         then:
-        1 * executer.execute(task, _ as TaskStateInternal)
+        1 * executer.execute(task, _ as TaskStateInternal, _ as TaskExecutionContext)
 
     }
 
@@ -192,7 +190,7 @@ public abstract class AbstractSpockTaskTest extends Specification {
         task.getOnlyIf().isSatisfiedBy(task)
 
         when:
-        task.onlyIf(HelperUtil.toClosure("{ task -> false }"));
+        task.onlyIf(TestUtil.toClosure("{ task -> false }"));
 
         then:
         assertFalse(task.getOnlyIf().isSatisfiedBy(task));
diff --git a/subprojects/core/src/testFixtures/groovy/org/gradle/api/tasks/AbstractTaskTest.java b/subprojects/core/src/testFixtures/groovy/org/gradle/api/tasks/AbstractTaskTest.java
index 329f2de..2230e43 100644
--- a/subprojects/core/src/testFixtures/groovy/org/gradle/api/tasks/AbstractTaskTest.java
+++ b/subprojects/core/src/testFixtures/groovy/org/gradle/api/tasks/AbstractTaskTest.java
@@ -29,13 +29,14 @@ import org.gradle.api.internal.project.DefaultProject;
 import org.gradle.api.internal.project.ProjectInternal;
 import org.gradle.api.internal.project.taskfactory.ITaskFactory;
 import org.gradle.api.internal.tasks.TaskExecuter;
+import org.gradle.api.internal.tasks.TaskExecutionContext;
 import org.gradle.api.internal.tasks.TaskStateInternal;
 import org.gradle.api.specs.Spec;
 import org.gradle.internal.reflect.Instantiator;
 import org.gradle.internal.service.DefaultServiceRegistry;
 import org.gradle.test.fixtures.file.TestNameTestDirectoryProvider;
 import org.gradle.util.GUtil;
-import org.gradle.util.HelperUtil;
+import org.gradle.util.TestUtil;
 import org.gradle.util.JUnit4GroovyMockery;
 import org.jmock.Expectations;
 import org.jmock.lib.legacy.ClassImposteriser;
@@ -49,11 +50,8 @@ import static org.hamcrest.Matchers.notNullValue;
 import static org.hamcrest.Matchers.sameInstance;
 import static org.junit.Assert.*;
 
-/**
- * @author Hans Dockter
- */
 public abstract class AbstractTaskTest {
-    public static final String TEST_TASK_NAME = "taskname";
+    public static final String TEST_TASK_NAME = "testTask";
     @Rule
     public TestNameTestDirectoryProvider tmpDir = new TestNameTestDirectoryProvider();
 
@@ -65,7 +63,7 @@ public abstract class AbstractTaskTest {
 
     protected Instantiator instantiator = new DependencyInjectingInstantiator(serviceRegistry);
 
-    private AbstractProject project = HelperUtil.createRootProject();
+    private AbstractProject project = TestUtil.createRootProject();
 
     public abstract AbstractTask getTask();
 
@@ -103,10 +101,10 @@ public abstract class AbstractTaskTest {
 
     @Test
     public void testPath() {
-        DefaultProject rootProject = HelperUtil.createRootProject();
-        DefaultProject childProject = HelperUtil.createChildProject(rootProject, "child");
+        DefaultProject rootProject = TestUtil.createRootProject();
+        DefaultProject childProject = TestUtil.createChildProject(rootProject, "child");
         childProject.getProjectDir().mkdirs();
-        DefaultProject childchildProject = HelperUtil.createChildProject(childProject, "childchild");
+        DefaultProject childchildProject = TestUtil.createChildProject(childProject, "childchild");
         childchildProject.getProjectDir().mkdirs();
 
         Task task = createTask(rootProject, TEST_TASK_NAME);
@@ -145,7 +143,7 @@ public abstract class AbstractTaskTest {
         task.setExecuter(executer);
 
         context.checking(new Expectations() {{
-            one(executer).execute(with(sameInstance(task)), with(notNullValue(TaskStateInternal.class)));
+            one(executer).execute(with(sameInstance(task)), with(notNullValue(TaskStateInternal.class)), with(notNullValue(TaskExecutionContext.class)));
         }});
 
         task.execute();
@@ -171,7 +169,7 @@ public abstract class AbstractTaskTest {
         AbstractTask task = getTask();
         assertTrue(task.getOnlyIf().isSatisfiedBy(task));
 
-        task.onlyIf(HelperUtil.toClosure("{ task -> false }"));
+        task.onlyIf(TestUtil.toClosure("{ task -> false }"));
         assertFalse(task.getOnlyIf().isSatisfiedBy(task));
     }
 
diff --git a/subprojects/core/src/testFixtures/groovy/org/gradle/api/tasks/bundling/AbstractArchiveTaskTest.groovy b/subprojects/core/src/testFixtures/groovy/org/gradle/api/tasks/bundling/AbstractArchiveTaskTest.groovy
index be42d17..8c2d36a 100644
--- a/subprojects/core/src/testFixtures/groovy/org/gradle/api/tasks/bundling/AbstractArchiveTaskTest.groovy
+++ b/subprojects/core/src/testFixtures/groovy/org/gradle/api/tasks/bundling/AbstractArchiveTaskTest.groovy
@@ -16,24 +16,20 @@
 
 package org.gradle.api.tasks.bundling
 
-import org.gradle.api.internal.ConventionTask
 import org.gradle.api.internal.file.FileResolver
-import org.gradle.api.tasks.AbstractConventionTaskTest
+import org.gradle.api.tasks.AbstractCopyTaskContractTest
 import org.junit.Test
 
 import static org.junit.Assert.assertEquals
 import static org.junit.Assert.assertTrue
 
-/**
- * @author Hans Dockter
- */
-abstract class AbstractArchiveTaskTest extends AbstractConventionTaskTest {
+abstract class AbstractArchiveTaskTest extends AbstractCopyTaskContractTest {
 
     FileResolver resolver = [resolve: {it as File}] as FileResolver
 
     abstract AbstractArchiveTask getArchiveTask()
 
-    ConventionTask getTask() {
+    AbstractArchiveTask getTask() {
         archiveTask
     }
 
diff --git a/subprojects/core/src/testFixtures/groovy/org/gradle/cache/internal/DefaultFileLockManagerTestHelper.groovy b/subprojects/core/src/testFixtures/groovy/org/gradle/cache/internal/DefaultFileLockManagerTestHelper.groovy
index 125fd31..fbab657 100644
--- a/subprojects/core/src/testFixtures/groovy/org/gradle/cache/internal/DefaultFileLockManagerTestHelper.groovy
+++ b/subprojects/core/src/testFixtures/groovy/org/gradle/cache/internal/DefaultFileLockManagerTestHelper.groovy
@@ -16,6 +16,8 @@
 
 package org.gradle.cache.internal
 
+import org.gradle.cache.internal.locklistener.NoOpFileLockContentionHandler
+
 abstract class DefaultFileLockManagerTestHelper {
 
     private static class AnException extends RuntimeException {}
@@ -51,13 +53,13 @@ abstract class DefaultFileLockManagerTestHelper {
             String getProcessDisplayName() {
                 return "process"
             }
-        })
+        }, new NoOpFileLockContentionHandler())
     }
     
     static FileLock createDefaultFileLock(File file, FileLockManager.LockMode mode = FileLockManager.LockMode.Exclusive, DefaultFileLockManager manager = createDefaultFileLockManager()) {
-        manager.lock(file, mode, "test lock")        
+        manager.lock(file, mode, "test lock")
     }
-    
+
     static File getLockFile(File target) {
         new File(target.absolutePath + ".lock")
     }
diff --git a/subprojects/core/src/testFixtures/groovy/org/gradle/util/ConcurrentSpecification.groovy b/subprojects/core/src/testFixtures/groovy/org/gradle/util/ConcurrentSpecification.groovy
index 5e334f5..0ad11fb 100644
--- a/subprojects/core/src/testFixtures/groovy/org/gradle/util/ConcurrentSpecification.groovy
+++ b/subprojects/core/src/testFixtures/groovy/org/gradle/util/ConcurrentSpecification.groovy
@@ -19,9 +19,6 @@ import org.gradle.test.fixtures.ConcurrentTestUtil
 import org.junit.Rule
 import spock.lang.Specification
 
-/**
- * by Szczepan Faber, created at: 12/8/11
- */
 class ConcurrentSpecification extends Specification {
     @Rule @Delegate ConcurrentTestUtil concurrent = new ConcurrentTestUtil()
 }
diff --git a/subprojects/core/src/testFixtures/groovy/org/gradle/util/HelperUtil.groovy b/subprojects/core/src/testFixtures/groovy/org/gradle/util/HelperUtil.groovy
deleted file mode 100644
index 8b24c26..0000000
--- a/subprojects/core/src/testFixtures/groovy/org/gradle/util/HelperUtil.groovy
+++ /dev/null
@@ -1,143 +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.util
-
-import org.apache.ivy.core.module.descriptor.Configuration
-import org.apache.ivy.core.module.descriptor.DefaultModuleDescriptor
-import org.apache.ivy.core.module.id.ModuleRevisionId
-import org.codehaus.groovy.control.CompilerConfiguration
-import org.gradle.api.Task
-import org.gradle.api.internal.project.DefaultProject
-import org.gradle.api.internal.project.ProjectInternal
-import org.gradle.api.internal.project.taskfactory.ITaskFactory
-import org.gradle.groovy.scripts.DefaultScript
-import org.gradle.groovy.scripts.Script
-import org.gradle.groovy.scripts.ScriptSource
-import org.gradle.test.fixtures.file.TestNameTestDirectoryProvider
-import org.gradle.testfixtures.ProjectBuilder
-
-import java.rmi.server.UID
-
-/**
- * @author Hans Dockter
- */
-class HelperUtil {
-     public static final Closure TEST_CLOSURE = {}
-
-     static <T extends Task> T createTask(Class<T> type) {
-         return createTask(type, createRootProject())
-     }
-
-     static <T extends Task> T createTask(Class<T> type, Map taskFields) {
-         def task = createTask(type, createRootProject())
-         hackInTaskProperties(type, task, taskFields)
-         return task
-     }
-
-    private static void hackInTaskProperties(Class type, Task task, Map args) {
-        args.each { k, v ->
-            def field = type.getDeclaredField(k)
-            field.setAccessible(true)
-            field.set(task, v)
-        }
-    }
-
-    static <T extends Task> T createTask(Class<T> type, ProjectInternal project) {
-         return createTask(type, project, 'name')
-     }
-
-     static <T extends Task> T createTask(Class<T> type, ProjectInternal project, String name) {
-         return project.services.get(ITaskFactory).createTask([name: name, type: type])
-     }
-
-    static ProjectBuilder builder() {
-        return ProjectBuilder.builder().withProjectDir(TestNameTestDirectoryProvider.newInstance().testDirectory)
-    }
-
-     static DefaultProject createRootProject() {
-         createRootProject(TestNameTestDirectoryProvider.newInstance().testDirectory)
-     }
-
-     static DefaultProject createRootProject(File rootDir) {
-         return ProjectBuilder
-                 .builder()
-                 .withProjectDir(rootDir)
-                 .build()
-     }
-
-     static DefaultProject createChildProject(DefaultProject parent, String name, File projectDir = null) {
-         return ProjectBuilder
-                 .builder()
-                 .withName(name)
-                 .withParent(parent)
-                 .withProjectDir(projectDir)
-                 .build();
-     }
-
-     static DefaultModuleDescriptor createModuleDescriptor(Set confs) {
-         DefaultModuleDescriptor moduleDescriptor = new DefaultModuleDescriptor(ModuleRevisionId.newInstance('org', 'name', 'rev'), "status", null)
-         confs.each { moduleDescriptor.addConfiguration(new Configuration(it)) }
-         return moduleDescriptor;
-     }
-
-     static groovy.lang.Script createScript(String code) {
-         new GroovyShell().parse(code)
-     }
-
-     static Object call(String text, Object... params) {
-         toClosure(text).call(*params)
-     }
-
-     static Closure toClosure(String text) {
-         return new GroovyShell().evaluate("return " + text)
-     }
-
-     static Closure toClosure(ScriptSource source) {
-         CompilerConfiguration configuration = new CompilerConfiguration();
-         configuration.setScriptBaseClass(TestScript.getName());
-
-         GroovyShell shell = new GroovyShell(configuration)
-         Script script = shell.parse(source.resource.text)
-         script.setScriptSource(source)
-         return script.run()
-     }
-
-     static Closure toClosure(TestClosure closure) {
-         return { param -> closure.call(param) }
-     }
-
-     static Closure returns(Object value) {
-         return { value }
-     }
-
-     static Closure createSetterClosure(String name, String value) {
-         return {
-             "set$name"(value)
-         }
-     }
-
-     static String createUniqueId() {
-         return new UID().toString();
-     }
- }
-
-
-interface TestClosure {
-    Object call(Object param);
-}
-
-abstract class TestScript extends DefaultScript {
-}
diff --git a/subprojects/core/src/testFixtures/groovy/org/gradle/util/TestTask.groovy b/subprojects/core/src/testFixtures/groovy/org/gradle/util/TestTask.groovy
index eadda17..eb6d41a 100644
--- a/subprojects/core/src/testFixtures/groovy/org/gradle/util/TestTask.groovy
+++ b/subprojects/core/src/testFixtures/groovy/org/gradle/util/TestTask.groovy
@@ -18,9 +18,6 @@ package org.gradle.util
 
 import org.gradle.api.internal.ConventionTask
 
-/**
- * @author Hans Dockter
- */
 class TestTask extends ConventionTask  {
     TestTask self
     String customProp
diff --git a/subprojects/core/src/testFixtures/groovy/org/gradle/util/TestUtil.groovy b/subprojects/core/src/testFixtures/groovy/org/gradle/util/TestUtil.groovy
new file mode 100644
index 0000000..12f568c
--- /dev/null
+++ b/subprojects/core/src/testFixtures/groovy/org/gradle/util/TestUtil.groovy
@@ -0,0 +1,140 @@
+/*
+ * Copyright 2010 the original author or authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.gradle.util
+
+import org.apache.ivy.core.module.descriptor.Configuration
+import org.apache.ivy.core.module.descriptor.DefaultModuleDescriptor
+import org.apache.ivy.core.module.id.ModuleRevisionId
+import org.codehaus.groovy.control.CompilerConfiguration
+import org.gradle.api.Task
+import org.gradle.api.internal.project.DefaultProject
+import org.gradle.api.internal.project.ProjectInternal
+import org.gradle.api.internal.project.taskfactory.ITaskFactory
+import org.gradle.groovy.scripts.DefaultScript
+import org.gradle.groovy.scripts.Script
+import org.gradle.groovy.scripts.ScriptSource
+import org.gradle.test.fixtures.file.TestNameTestDirectoryProvider
+import org.gradle.testfixtures.ProjectBuilder
+
+import java.rmi.server.UID
+
+class TestUtil {
+     public static final Closure TEST_CLOSURE = {}
+
+     static <T extends Task> T createTask(Class<T> type) {
+         return createTask(type, createRootProject())
+     }
+
+     static <T extends Task> T createTask(Class<T> type, Map taskFields) {
+         def task = createTask(type, createRootProject())
+         hackInTaskProperties(type, task, taskFields)
+         return task
+     }
+
+    private static void hackInTaskProperties(Class type, Task task, Map args) {
+        args.each { k, v ->
+            def field = type.getDeclaredField(k)
+            field.setAccessible(true)
+            field.set(task, v)
+        }
+    }
+
+    static <T extends Task> T createTask(Class<T> type, ProjectInternal project) {
+         return createTask(type, project, 'name')
+     }
+
+     static <T extends Task> T createTask(Class<T> type, ProjectInternal project, String name) {
+         return project.services.get(ITaskFactory).createTask([name: name, type: type])
+     }
+
+    static ProjectBuilder builder() {
+        return ProjectBuilder.builder().withProjectDir(TestNameTestDirectoryProvider.newInstance().testDirectory)
+    }
+
+     static DefaultProject createRootProject() {
+         createRootProject(TestNameTestDirectoryProvider.newInstance().testDirectory)
+     }
+
+     static DefaultProject createRootProject(File rootDir) {
+         return ProjectBuilder
+                 .builder()
+                 .withProjectDir(rootDir)
+                 .build()
+     }
+
+     static DefaultProject createChildProject(DefaultProject parent, String name, File projectDir = null) {
+         return ProjectBuilder
+                 .builder()
+                 .withName(name)
+                 .withParent(parent)
+                 .withProjectDir(projectDir)
+                 .build();
+     }
+
+     static DefaultModuleDescriptor createModuleDescriptor(Set confs) {
+         DefaultModuleDescriptor moduleDescriptor = new DefaultModuleDescriptor(ModuleRevisionId.newInstance('org', 'name', 'rev'), "status", null)
+         confs.each { moduleDescriptor.addConfiguration(new Configuration(it)) }
+         return moduleDescriptor;
+     }
+
+     static groovy.lang.Script createScript(String code) {
+         new GroovyShell().parse(code)
+     }
+
+     static Object call(String text, Object... params) {
+         toClosure(text).call(*params)
+     }
+
+     static Closure toClosure(String text) {
+         return new GroovyShell().evaluate("return " + text)
+     }
+
+     static Closure toClosure(ScriptSource source) {
+         CompilerConfiguration configuration = new CompilerConfiguration();
+         configuration.setScriptBaseClass(TestScript.getName());
+
+         GroovyShell shell = new GroovyShell(configuration)
+         Script script = shell.parse(source.resource.text)
+         script.setScriptSource(source)
+         return script.run()
+     }
+
+     static Closure toClosure(TestClosure closure) {
+         return { param -> closure.call(param) }
+     }
+
+     static Closure returns(Object value) {
+         return { value }
+     }
+
+     static Closure createSetterClosure(String name, String value) {
+         return {
+             "set$name"(value)
+         }
+     }
+
+     static String createUniqueId() {
+         return new UID().toString();
+     }
+ }
+
+
+interface TestClosure {
+    Object call(Object param);
+}
+
+abstract class TestScript extends DefaultScript {
+}
diff --git a/subprojects/cpp/cpp.gradle b/subprojects/cpp/cpp.gradle
index 0bfc388..d377cf8 100644
--- a/subprojects/cpp/cpp.gradle
+++ b/subprojects/cpp/cpp.gradle
@@ -15,10 +15,11 @@
  */
 
 dependencies {
-    groovy libraries.groovy
+    compile libraries.groovy
     compile project(':core')
     compile project(":plugins")
     compile project(":ide")
+    compile libraries.commons_io
     integTestRuntime project(":maven")
 }
 
@@ -30,3 +31,4 @@ integTestTasks.all {
 }
 
 useTestFixtures()
+//useClassycle()
diff --git a/subprojects/cpp/src/integTest/groovy/org/gradle/nativecode/language/cpp/AbstractLanguageIncrementalBuildIntegrationTest.groovy b/subprojects/cpp/src/integTest/groovy/org/gradle/nativecode/language/cpp/AbstractLanguageIncrementalBuildIntegrationTest.groovy
new file mode 100755
index 0000000..1622573
--- /dev/null
+++ b/subprojects/cpp/src/integTest/groovy/org/gradle/nativecode/language/cpp/AbstractLanguageIncrementalBuildIntegrationTest.groovy
@@ -0,0 +1,327 @@
+/*
+ * Copyright 2013 the original author or authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+
+
+
+package org.gradle.nativecode.language.cpp
+
+import org.gradle.internal.os.OperatingSystem
+import org.gradle.nativecode.language.cpp.fixtures.AbstractInstalledToolChainIntegrationSpec
+import org.gradle.nativecode.language.cpp.fixtures.app.IncrementalHelloWorldApp
+import org.gradle.test.fixtures.file.TestFile
+import org.gradle.util.Requires
+import org.gradle.util.TestPrecondition
+
+import static org.gradle.util.TextUtil.escapeString
+
+abstract class AbstractLanguageIncrementalBuildIntegrationTest extends AbstractInstalledToolChainIntegrationSpec {
+    IncrementalHelloWorldApp app
+    String mainCompileTask
+    String libraryCompileTask
+    TestFile sourceFile
+    TestFile headerFile
+    List<TestFile> librarySourceFiles = []
+
+    abstract IncrementalHelloWorldApp getHelloWorldApp();
+
+    def "setup"() {
+        app = getHelloWorldApp()
+        mainCompileTask = ":compileMainExecutableMain${app.sourceType}"
+        libraryCompileTask = ":compileHelloSharedLibraryHello${app.sourceType}"
+
+        buildFile << """
+            apply plugin: 'cpp'
+
+            sources {
+                main {}
+                hello {}
+            }
+
+            executables {
+                main {
+                    source sources.main
+                }
+            }
+            libraries {
+                hello {
+                    source sources.hello
+                    binaries.withType(SharedLibraryBinary) {
+                        define "DLL_EXPORT"
+                    }
+                }
+            }
+            sources.main.cpp.lib libraries.hello
+        """
+        settingsFile << "rootProject.name = 'test'"
+
+        sourceFile = app.mainSource.writeToDir(file("src/main"))
+        headerFile = app.libraryHeader.writeToDir(file("src/hello"))
+        app.librarySources.each {
+            librarySourceFiles << it.writeToDir(file("src/hello"))
+        }
+
+        run "installMainExecutable"
+    }
+
+    def "does not re-execute build with no change"() {
+        when:
+        run "installMainExecutable"
+
+        then:
+        nonSkippedTasks.empty
+    }
+
+    @Requires(TestPrecondition.CAN_INSTALL_EXECUTABLE)
+    def "rebuilds binary with source file change"() {
+        given:
+        def install = installation("build/install/mainExecutable")
+
+        when:
+        sourceFile.text = app.alternateMainSource.content
+
+        and:
+        run "installMainExecutable"
+
+        then:
+        skipped libraryCompileTask
+        skipped ":linkHelloSharedLibrary"
+        skipped ":helloSharedLibrary"
+        executedAndNotSkipped mainCompileTask
+        executedAndNotSkipped ":linkMainExecutable"
+        executedAndNotSkipped ":mainExecutable"
+        executedAndNotSkipped ":installMainExecutable"
+
+        and:
+        install.assertInstalled()
+        install.exec().out == app.alternateOutput
+    }
+
+    @Requires(TestPrecondition.CAN_INSTALL_EXECUTABLE)
+    def "relinks binary with library source file change"() {
+        // sleep for 1 second to ensure generated binaries have different timestamp
+        sleep(1000)
+
+        when:
+        def install = installation("build/install/mainExecutable")
+        for (int i = 0; i < librarySourceFiles.size(); i++) {
+            TestFile sourceFile = librarySourceFiles.get(i);
+            sourceFile.text = app.alternateLibrarySources[i].content
+        }
+
+        and:
+        run "installMainExecutable"
+
+        then:
+        executedAndNotSkipped libraryCompileTask
+        executedAndNotSkipped ":linkHelloSharedLibrary"
+        executedAndNotSkipped ":helloSharedLibrary"
+        skipped mainCompileTask
+        executedAndNotSkipped ":linkMainExecutable"
+        executedAndNotSkipped ":mainExecutable"
+        executedAndNotSkipped ":installMainExecutable"
+
+        and:
+        install.assertInstalled()
+        install.exec().out == app.alternateLibraryOutput
+    }
+
+    def "recompiles binary when header file changes in a way that does not affect the object files"() {
+        if (toolChain.visualCpp) {
+            return // Visual C++ compiler embeds a timestamp in every object file, so relinking is always required after recompiling
+        }
+
+        when:
+        headerFile << """
+// Comment added to the end of the header file
+"""
+
+        run "installMainExecutable"
+
+        then:
+        executedAndNotSkipped libraryCompileTask
+        skipped ":linkHelloSharedLibrary"
+        skipped ":helloSharedLibrary"
+        executedAndNotSkipped mainCompileTask
+        skipped ":linkMainExecutable"
+        skipped ":mainExecutable"
+        skipped ":installMainExecutable"
+    }
+
+    @Requires(TestPrecondition.CAN_INSTALL_EXECUTABLE)
+    def "rebuilds binary with compiler option change"() {
+        when:
+        def install = installation("build/install/mainExecutable")
+
+        and:
+        buildFile << """
+            libraries {
+                hello {
+                    binaries.all { compilerArgs '-DFRENCH' }
+                }
+            }
+"""
+
+        run "installMainExecutable"
+
+        then:
+        executedAndNotSkipped libraryCompileTask
+        executedAndNotSkipped ":linkHelloSharedLibrary"
+        executedAndNotSkipped ":helloSharedLibrary"
+        skipped mainCompileTask
+        executedAndNotSkipped ":linkMainExecutable"
+        executedAndNotSkipped ":mainExecutable"
+        executedAndNotSkipped ":installMainExecutable"
+
+        and:
+        install.assertInstalled()
+        install.exec().out == app.frenchOutput
+    }
+
+    def "relinks binary when set of input libraries changes"() {
+        def executable = executable("build/binaries/mainExecutable/main")
+        def snapshot = executable.snapshot()
+
+        when:
+        buildFile << """
+            executables {
+                main {
+                    binaries.all {
+                        lib libraries.hello.static
+                    }
+                }
+            }
+"""
+
+        run "installMainExecutable"
+
+        then:
+        skipped libraryCompileTask
+        skipped ":linkHelloSharedLibrary"
+        skipped ":helloSharedLibrary"
+        skipped mainCompileTask
+        executedAndNotSkipped ":linkMainExecutable"
+        executedAndNotSkipped ":mainExecutable"
+        executedAndNotSkipped ":installMainExecutable"
+
+        and:
+        executable.assertHasChangedSince(snapshot)
+    }
+
+    def "relinks binary but does not recompile when linker option changed"() {
+        when:
+        def executable = executable("build/binaries/mainExecutable/main")
+        def snapshot = executable.snapshot()
+
+        and:
+        def linkerArgs = toolChain.isVisualCpp() ? "'/DEBUG'" : OperatingSystem.current().isMacOsX() ? "'-no_pie'" : "'-q'";
+        linkerArgs = escapeString(linkerArgs)
+        buildFile << """
+            executables {
+                main {
+                    binaries.all { linkerArgs ${escapeString(linkerArgs)} }
+                }
+            }
+"""
+
+        run "installMainExecutable"
+
+        then:
+        skipped libraryCompileTask
+        skipped ":linkHelloSharedLibrary"
+        skipped ":helloSharedLibrary"
+        skipped mainCompileTask
+        executedAndNotSkipped ":linkMainExecutable"
+        executedAndNotSkipped ":mainExecutable"
+        executedAndNotSkipped ":installMainExecutable"
+
+        and:
+        executable.assertExists()
+        executable.assertHasChangedSince(snapshot)
+    }
+
+    def "recompiles source but does not relink binary with source comment change"() {
+        if (toolChain.visualCpp) {
+            return // Visual C++ compiler embeds a timestamp in every object file, so relinking is always required after recompiling
+        }
+        when:
+        sourceFile.text = sourceFile.text.replaceFirst("// Simple hello world app", "// Comment is changed")
+        run "installMainExecutable"
+
+        then:
+        skipped libraryCompileTask
+        skipped ":linkHelloSharedLibrary"
+        skipped ":helloSharedLibrary"
+        executedAndNotSkipped mainCompileTask
+        skipped ":linkMainExecutable"
+        skipped ":mainExecutable"
+        skipped ":installMainExecutable"
+    }
+
+    def "cleans up stale object files when source file renamed"() {
+        def oldObjFile = objectFile("build/objectFiles/mainExecutable/main${app.sourceType}/main")
+        def newObjFile = objectFile("build/objectFiles/mainExecutable/main${app.sourceType}/changed_main")
+        assert oldObjFile.file
+        assert !newObjFile.file
+
+        when:
+        sourceFile.renameTo("${sourceFile.parentFile.absolutePath}/changed_${sourceFile.name}")
+        run "mainExecutable"
+
+        then:
+        skipped libraryCompileTask
+        skipped ":linkHelloSharedLibrary"
+        skipped ":helloSharedLibrary"
+        executedAndNotSkipped mainCompileTask
+        executedAndNotSkipped ":linkMainExecutable"
+        executedAndNotSkipped ":mainExecutable"
+
+        and:
+        !oldObjFile.file
+        newObjFile.file
+    }
+
+    def "cleans up stale debug files when changing from debug to non-debug"() {
+        if (!toolChain.visualCpp) {
+            return
+        }
+
+        given:
+        buildFile << """
+            binaries.all { compilerArgs '/Zi'; linkerArgs '/DEBUG'; }
+        """
+        run "mainExecutable"
+
+        def executable = executable("build/binaries/mainExecutable/main")
+        executable.assertDebugFileExists()
+
+        when:
+        buildFile << """
+            binaries.all { compilerArgs.clear(); linkerArgs.clear(); }
+        """
+        run "mainExecutable"
+
+        then:
+        executedAndNotSkipped libraryCompileTask
+        executedAndNotSkipped ":helloSharedLibrary"
+        executedAndNotSkipped mainCompileTask
+        executedAndNotSkipped ":linkMainExecutable"
+        executedAndNotSkipped ":mainExecutable"
+
+        and:
+        executable.assertDebugFileDoesNotExist()
+    }
+}
diff --git a/subprojects/cpp/src/integTest/groovy/org/gradle/nativecode/language/cpp/AbstractLanguageIntegrationTest.groovy b/subprojects/cpp/src/integTest/groovy/org/gradle/nativecode/language/cpp/AbstractLanguageIntegrationTest.groovy
new file mode 100755
index 0000000..2eaf887
--- /dev/null
+++ b/subprojects/cpp/src/integTest/groovy/org/gradle/nativecode/language/cpp/AbstractLanguageIntegrationTest.groovy
@@ -0,0 +1,208 @@
+/*
+ * Copyright 2013 the original author or authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+
+package org.gradle.nativecode.language.cpp
+import org.gradle.nativecode.language.cpp.fixtures.AbstractInstalledToolChainIntegrationSpec
+import org.gradle.nativecode.language.cpp.fixtures.app.HelloWorldApp
+import org.gradle.util.Requires
+import org.gradle.util.TestPrecondition
+
+abstract class AbstractLanguageIntegrationTest extends AbstractInstalledToolChainIntegrationSpec {
+
+    abstract HelloWorldApp getHelloWorldApp()
+
+    def "compile and link executable"() {
+        given:
+        buildFile << """
+            apply plugin: "cpp"
+            sources {
+                main {}
+            }
+            executables {
+                main {
+                    source sources.main
+                }
+            }
+            binaries.all {
+                $helloWorldApp.customArgs
+            }
+        """
+
+        and:
+        helloWorldApp.writeSources(file("src/main"))
+
+        when:
+        run "mainExecutable"
+
+        then:
+        def mainExecutable = executable("build/binaries/mainExecutable/main")
+        mainExecutable.assertExists()
+        mainExecutable.exec().out == helloWorldApp.englishOutput
+    }
+
+    def "build executable with custom compiler arg"() {
+        given:
+        buildFile << """
+            apply plugin: "cpp"
+            sources {
+                main {}
+            }
+            executables {
+                main {
+                    source sources.main
+                    binaries.all {
+                        compilerArgs "-DFRENCH"
+                    }
+                }
+            }
+            binaries.all {
+                $helloWorldApp.customArgs
+            }
+        """
+
+        and:
+        helloWorldApp.writeSources(file("src/main"))
+
+        when:
+        run "mainExecutable"
+
+        then:
+        def mainExecutable = executable("build/binaries/mainExecutable/main")
+        mainExecutable.assertExists()
+        mainExecutable.exec().out == helloWorldApp.frenchOutput
+    }
+
+    def "build executable with macro defined"() {
+        given:
+        buildFile << """
+            apply plugin: "cpp"
+            sources {
+                main {}
+            }
+            executables {
+                main {
+                    source sources.main
+                    binaries.all {
+                        define "FRENCH"
+                    }
+                }
+            }
+            binaries.all {
+                $helloWorldApp.customArgs
+            }
+        """
+
+        and:
+        helloWorldApp.writeSources(file("src/main"))
+
+        when:
+        run "mainExecutable"
+
+        then:
+        def mainExecutable = executable("build/binaries/mainExecutable/main")
+        mainExecutable.assertExists()
+        mainExecutable.exec().out == helloWorldApp.frenchOutput
+    }
+
+    @Requires(TestPrecondition.CAN_INSTALL_EXECUTABLE)
+    def "build shared library and link into executable"() {
+        given:
+        buildFile << """
+            apply plugin: "cpp"
+
+            sources {
+                main {}
+                hello {}
+            }
+            executables {
+                main {
+                    source sources.main
+                }
+            }
+            libraries {
+                hello {
+                    source sources.hello
+                }
+            }
+            sources.main.c.lib libraries.hello
+            binaries.all {
+                $helloWorldApp.customArgs
+            }
+        """
+
+        and:
+        helloWorldApp.writeSources(file("src/main"), file("src/hello"))
+
+        when:
+        run "installMainExecutable"
+
+        then:
+        sharedLibrary("build/binaries/helloSharedLibrary/hello").assertExists()
+        executable("build/binaries/mainExecutable/main").assertExists()
+
+        def install = installation("build/install/mainExecutable")
+        install.assertInstalled()
+        install.assertIncludesLibraries("hello")
+        install.exec().out == helloWorldApp.englishOutput
+    }
+
+    @Requires(TestPrecondition.CAN_INSTALL_EXECUTABLE)
+    def "build static library and link into executable"() {
+        given:
+        buildFile << """
+            apply plugin: "cpp"
+
+            sources {
+                main {}
+                hello {}
+            }
+            executables {
+                main {
+                    source sources.main
+                }
+            }
+            libraries {
+                hello {
+                    source sources.hello
+                    binaries.withType(StaticLibraryBinary) {
+                        define "FRENCH"
+                    }
+                }
+            }
+            sources.main.c.lib libraries.hello.static
+            binaries.all {
+                $helloWorldApp.customArgs
+            }
+        """
+
+        and:
+        helloWorldApp.writeSources(file("src/main"), file("src/hello"))
+
+        when:
+        run "installMainExecutable"
+
+        then:
+        staticLibrary("build/binaries/helloStaticLibrary/hello").assertExists()
+        executable("build/binaries/mainExecutable/main").assertExists()
+
+        and:
+        def install = installation("build/install/mainExecutable")
+        install.assertInstalled()
+        install.exec().out == helloWorldApp.frenchOutput
+    }
+}
+
diff --git a/subprojects/cpp/src/integTest/groovy/org/gradle/nativecode/language/cpp/AssemblyLanguageIntegrationTest.groovy b/subprojects/cpp/src/integTest/groovy/org/gradle/nativecode/language/cpp/AssemblyLanguageIntegrationTest.groovy
new file mode 100755
index 0000000..15aae95
--- /dev/null
+++ b/subprojects/cpp/src/integTest/groovy/org/gradle/nativecode/language/cpp/AssemblyLanguageIntegrationTest.groovy
@@ -0,0 +1,78 @@
+/*
+ * Copyright 2013 the original author or authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+
+package org.gradle.nativecode.language.cpp
+
+import org.gradle.nativecode.language.cpp.fixtures.AvailableToolChains
+import org.gradle.nativecode.language.cpp.fixtures.app.HelloWorldApp
+import org.gradle.nativecode.language.cpp.fixtures.app.MixedLanguageHelloWorldApp
+import org.gradle.nativecode.language.cpp.fixtures.app.SourceFile
+
+class AssemblyLanguageIntegrationTest extends AbstractLanguageIntegrationTest {
+
+    HelloWorldApp helloWorldApp = new AssemblerWithCHelloWorldApp(toolChain)
+
+    def "build fails when compilation fails"() {
+        given:
+        buildFile << """
+            apply plugin: "cpp"
+            sources {
+                main {}
+            }
+            executables {
+                main {
+                    source sources.main
+                }
+            }
+        """
+
+        and:
+        file("src/main/asm/broken.s") << """
+.section    __TEXT,__text,regular,pure_instructions
+.globl  _sum
+.align  4, 0x90
+_sum:
+pushl
+"""
+
+        expect:
+        fails "mainExecutable"
+        failure.assertHasDescription("Execution failed for task ':assembleMainExecutableMainAsm'.");
+        failure.assertHasCause("Assembler failed; see the error output for details.")
+    }
+
+    static class AssemblerWithCHelloWorldApp extends MixedLanguageHelloWorldApp {
+        AssemblerWithCHelloWorldApp(AvailableToolChains.InstalledToolChain toolChain) {
+            super(toolChain)
+        }
+
+        @Override
+        SourceFile getMainSource() {
+            return new SourceFile("c", "main.c", """
+                #include <stdio.h>
+                #include "hello.h"
+
+                int main () {
+                    sayHello();
+                    printf("%d", sum(5, 7));
+                    return 0;
+                }
+            """);
+        }
+    }
+}
+
diff --git a/subprojects/cpp/src/integTest/groovy/org/gradle/nativecode/language/cpp/BinaryFlavorsIntegrationTest.groovy b/subprojects/cpp/src/integTest/groovy/org/gradle/nativecode/language/cpp/BinaryFlavorsIntegrationTest.groovy
new file mode 100755
index 0000000..d7583c9
--- /dev/null
+++ b/subprojects/cpp/src/integTest/groovy/org/gradle/nativecode/language/cpp/BinaryFlavorsIntegrationTest.groovy
@@ -0,0 +1,217 @@
+/*
+ * Copyright 2013 the original author or authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.gradle.nativecode.language.cpp
+import org.gradle.nativecode.language.cpp.fixtures.AbstractInstalledToolChainIntegrationSpec
+import org.gradle.nativecode.language.cpp.fixtures.app.CppHelloWorldApp
+import org.gradle.util.Requires
+import org.gradle.util.TestPrecondition
+
+class BinaryFlavorsIntegrationTest extends AbstractInstalledToolChainIntegrationSpec {
+    def helloWorldApp = new CppHelloWorldApp()
+
+    def "setup"() {
+        settingsFile << "rootProject.name = 'test'"
+
+        helloWorldApp.writeSources(file("src/main"), file("src/hello"))
+    }
+
+    @Requires(TestPrecondition.CAN_INSTALL_EXECUTABLE)
+     def "build multiple flavors of executable binary and link library with no defined flavor"() {
+        when:
+        buildFile << """
+            apply plugin: "cpp"
+            sources {
+                main {}
+                hello {}
+            }
+            libraries {
+                hello {
+                    source sources.hello
+                    binaries.withType(StaticLibraryBinary) {
+                        define "FRENCH"
+                    }
+                }
+            }
+            executables {
+                main {
+                    source sources.main
+                    flavors {
+                        english {}
+                        french {}
+                    }
+                    binaries.all {
+                        if (flavor == flavors.french) {
+                            lib libraries.hello.static
+                        } else {
+                            lib libraries.hello.shared
+                        }
+                    }
+                }
+            }
+        """
+
+        and:
+        succeeds "installEnglishMainExecutable"
+
+        then:
+        installation("build/install/mainExecutable/english").exec().out == helloWorldApp.englishOutput
+
+        when:
+        succeeds "installFrenchMainExecutable"
+
+        then:
+        installation("build/install/mainExecutable/french").exec().out == helloWorldApp.frenchOutput
+    }
+
+    @Requires(TestPrecondition.CAN_INSTALL_EXECUTABLE)
+    def "build multiple flavors of shared library binary and link into executable with same flavor"() {
+        when:
+        buildFile << """
+            apply plugin: "cpp"
+            sources {
+                main {}
+                hello {}
+            }
+            libraries {
+                hello {
+                    source sources.hello
+                    flavors {
+                        english {}
+                        french {}
+                    }
+                    binaries.all {
+                        if (flavor == flavors.french) {
+                            define "FRENCH"
+                        }
+                    }
+                }
+            }
+            executables {
+                main {
+                    source sources.main
+                    flavors {
+                        english {}
+                        french {}
+                    }
+                    binaries.all {
+                        lib libraries.hello
+                    }
+                }
+            }
+        """
+
+        and:
+        succeeds "installEnglishMainExecutable"
+
+        then:
+        installation("build/install/mainExecutable/english").assertInstalled().exec().out == helloWorldApp.englishOutput
+
+        when:
+        succeeds "installFrenchMainExecutable"
+
+        then:
+        installation("build/install/mainExecutable/french").assertInstalled().exec().out == helloWorldApp.frenchOutput
+    }
+
+    def "build multiple flavors of static library binary and link into executable with same flavor"() {
+        when:
+        buildFile << """
+            apply plugin: "cpp"
+            sources {
+                main {}
+                hello {}
+            }
+            libraries {
+                hello {
+                    source sources.hello
+                    flavors {
+                        english {}
+                        french {}
+                    }
+                    binaries.all {
+                        if (flavor == flavors.french) {
+                            define "FRENCH"
+                        }
+                    }
+                }
+            }
+            executables {
+                main {
+                    source sources.main
+                    flavors {
+                        english {}
+                        french {}
+                    }
+                    binaries.all {
+                        lib libraries.hello.static
+                    }
+                }
+            }
+        """
+
+        and:
+        succeeds "englishMainExecutable"
+
+        then:
+        executable("build/binaries/mainExecutable/english/main").exec().out == helloWorldApp.englishOutput
+
+        when:
+        succeeds "frenchMainExecutable"
+
+        then:
+        executable("build/binaries/mainExecutable/french/main").exec().out == helloWorldApp.frenchOutput
+    }
+
+    def "build fails when library has no matching flavour"() {
+        when:
+        buildFile << """
+            apply plugin: "cpp"
+            sources {
+                main {}
+                hello {}
+            }
+            libraries {
+                hello {
+                    source sources.hello
+                    flavors {
+                        english {}
+                        french {}
+                    }
+                }
+            }
+            executables {
+                main {
+                    source sources.main
+                    flavors {
+                        english {}
+                        german {}
+                    }
+                    binaries.all {
+                        lib libraries.hello
+                    }
+                }
+            }
+        """
+
+        and:
+        // TODO:DAZ Fix the exception reporting
+        executer.withStackTraceChecksDisabled()
+
+        then:
+        fails "germanMainExecutable"
+        failure.assertHasCause("No shared library binary available for library 'hello' with flavor 'german'")
+   }
+}
diff --git a/subprojects/cpp/src/integTest/groovy/org/gradle/nativecode/language/cpp/CCallingCppLanguageIntegrationTest.groovy b/subprojects/cpp/src/integTest/groovy/org/gradle/nativecode/language/cpp/CCallingCppLanguageIntegrationTest.groovy
new file mode 100755
index 0000000..9ef741e
--- /dev/null
+++ b/subprojects/cpp/src/integTest/groovy/org/gradle/nativecode/language/cpp/CCallingCppLanguageIntegrationTest.groovy
@@ -0,0 +1,25 @@
+/*
+ * Copyright 2013 the original author or authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.gradle.nativecode.language.cpp
+
+import org.gradle.nativecode.language.cpp.fixtures.app.CCallingCppHelloWorldApp
+import org.gradle.nativecode.language.cpp.fixtures.app.HelloWorldApp
+
+class CCallingCppLanguageIntegrationTest extends AbstractLanguageIntegrationTest {
+    HelloWorldApp helloWorldApp = new CCallingCppHelloWorldApp()
+}
+
diff --git a/subprojects/cpp/src/integTest/groovy/org/gradle/nativecode/language/cpp/CLanguageIncrementalBuildIntegrationTest.groovy b/subprojects/cpp/src/integTest/groovy/org/gradle/nativecode/language/cpp/CLanguageIncrementalBuildIntegrationTest.groovy
new file mode 100755
index 0000000..5980fc9
--- /dev/null
+++ b/subprojects/cpp/src/integTest/groovy/org/gradle/nativecode/language/cpp/CLanguageIncrementalBuildIntegrationTest.groovy
@@ -0,0 +1,27 @@
+/*
+ * Copyright 2013 the original author or authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.gradle.nativecode.language.cpp
+
+import org.gradle.nativecode.language.cpp.fixtures.app.CHelloWorldApp
+import org.gradle.nativecode.language.cpp.fixtures.app.IncrementalHelloWorldApp
+
+class CLanguageIncrementalBuildIntegrationTest extends AbstractLanguageIncrementalBuildIntegrationTest {
+    @Override
+    IncrementalHelloWorldApp getHelloWorldApp() {
+        new CHelloWorldApp()
+    }
+}
diff --git a/subprojects/cpp/src/integTest/groovy/org/gradle/nativecode/language/cpp/CLanguageIntegrationTest.groovy b/subprojects/cpp/src/integTest/groovy/org/gradle/nativecode/language/cpp/CLanguageIntegrationTest.groovy
new file mode 100755
index 0000000..9f228cf
--- /dev/null
+++ b/subprojects/cpp/src/integTest/groovy/org/gradle/nativecode/language/cpp/CLanguageIntegrationTest.groovy
@@ -0,0 +1,52 @@
+/*
+ * Copyright 2013 the original author or authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.gradle.nativecode.language.cpp
+
+import org.gradle.nativecode.language.cpp.fixtures.app.CHelloWorldApp
+import org.gradle.nativecode.language.cpp.fixtures.app.HelloWorldApp
+
+class CLanguageIntegrationTest extends AbstractLanguageIntegrationTest {
+
+    HelloWorldApp helloWorldApp = new CHelloWorldApp()
+
+    def "build fails when compilation fails"() {
+        given:
+        buildFile << """
+             apply plugin: "cpp"
+             sources {
+                 main {}
+             }
+             executables {
+                 main {
+                     source sources.main
+                 }
+             }
+         """
+
+        and:
+        file("src/main/c/broken.c") << """
+        #include <stdio.h>
+
+        'broken
+"""
+
+        expect:
+        fails "mainExecutable"
+        failure.assertHasDescription("Execution failed for task ':compileMainExecutableMainC'.");
+        failure.assertHasCause("C compiler failed; see the error output for details.")
+    }
+}
+
diff --git a/subprojects/cpp/src/integTest/groovy/org/gradle/nativecode/language/cpp/CppBinariesIntegrationTest.groovy b/subprojects/cpp/src/integTest/groovy/org/gradle/nativecode/language/cpp/CppBinariesIntegrationTest.groovy
new file mode 100755
index 0000000..d794ea2
--- /dev/null
+++ b/subprojects/cpp/src/integTest/groovy/org/gradle/nativecode/language/cpp/CppBinariesIntegrationTest.groovy
@@ -0,0 +1,219 @@
+/*
+ * Copyright 2012 the original author or authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.gradle.nativecode.language.cpp
+
+import org.gradle.nativecode.language.cpp.fixtures.AbstractInstalledToolChainIntegrationSpec
+import org.gradle.util.Requires
+import org.gradle.util.TestPrecondition
+
+class CppBinariesIntegrationTest extends AbstractInstalledToolChainIntegrationSpec {
+    def "can configure the binaries of a C++ application"() {
+        given:
+        buildFile << """
+            apply plugin: "cpp-exe"
+
+            executables {
+                main {
+                    binaries.all {
+                        outputFile file('${executable("build/test").toURI()}')
+                        define 'ENABLE_GREETING'
+                    }
+                }
+            }
+        """
+        settingsFile << "rootProject.name = 'test'"
+
+        and:
+        file("src/main/cpp/helloworld.cpp") << """
+            #include <iostream>
+
+            int main () {
+              #ifdef ENABLE_GREETING
+              std::cout << "Hello!";
+              #endif
+              return 0;
+            }
+        """
+
+        when:
+        run "mainExecutable"
+
+        then:
+        def executable = executable("build/test")
+        executable.exec().out == "Hello!"
+    }
+
+    def "can build debug binaries for a C++ executable"() {
+        given:
+        buildFile << """
+            apply plugin: "cpp-exe"
+
+            executables {
+                main {
+                    binaries.all {
+                        if (toolChain in VisualCpp) {
+                            compilerArgs '/Zi'
+                            linkerArgs '/DEBUG'
+                        } else {
+                            compilerArgs '-g'
+                        }
+                    }
+                }
+            }
+        """
+        settingsFile << "rootProject.name = 'test'"
+
+        and:
+        file("src/main/cpp/helloworld.cpp") << """
+            #include <iostream>
+
+            int main () {
+              std::cout << "Hello!";
+              return 0;
+            }
+        """
+
+        when:
+        run "mainExecutable"
+
+        then:
+        def executable = executable("build/binaries/mainExecutable/test")
+        executable.exec().out == "Hello!"
+        executable.assertDebugFileExists()
+        // TODO - need to verify that the debug info ended up in the binary
+    }
+
+    @Requires(TestPrecondition.CAN_INSTALL_EXECUTABLE)
+    def "can configure the binaries of a C++ library"() {
+        given:
+        buildFile << """
+            apply plugin: "cpp-exe"
+
+            sources {
+                hello {}
+            }
+            libraries {
+                hello {
+                    source sources.hello.cpp
+                    binaries.all {
+                        outputFile file('${staticLibrary("build/hello").toURI()}')
+                        define 'ENABLE_GREETING'
+                    }
+                }
+            }
+            executables {
+                main {
+                    binaries.all {
+                        lib libraries.hello.static
+                    }
+                }
+            }
+        """
+        settingsFile << "rootProject.name = 'test'"
+
+        and:
+        file("src/hello/cpp/hello.cpp") << """
+            #include <iostream>
+
+            void hello(const char* str) {
+              #ifdef ENABLE_GREETING
+              std::cout << str;
+              #endif
+            }
+        """
+
+        and:
+        file("src/hello/headers/hello.h") << """
+            void hello(const char* str);
+        """
+
+        and:
+        file("src/main/cpp/main.cpp") << """
+            #include "hello.h"
+
+            int main () {
+              hello("Hello!");
+              return 0;
+            }
+        """
+
+        when:
+        run "installMainExecutable"
+
+        then:
+        staticLibrary("build/hello").assertExists()
+        installation("build/install/mainExecutable").exec().out == "Hello!"
+    }
+
+    def "can configure a binary to use additional source sets"() {
+        given:
+        buildFile << """
+            apply plugin: "cpp"
+
+            sources {
+                main {
+                    cpp {
+                        exportedHeaders.srcDir "src/shared/headers"
+                    }
+                }
+                util {
+                    cpp {
+                        exportedHeaders.srcDir "src/shared/headers"
+                    }
+                }
+            }
+            executables {
+                main {
+                    source sources.main.cpp
+                    binaries.all {
+                        source sources.util.cpp
+                    }
+                }
+            }
+        """
+        settingsFile << "rootProject.name = 'test'"
+
+        and:
+        file("src/shared/headers/greeting.h") << """
+            void greeting();
+"""
+
+        file("src/util/cpp/greeting.cpp") << """
+            #include <iostream>
+            #include "greeting.h"
+
+            void greeting() {
+                std::cout << "Hello!";
+            }
+        """
+
+        file("src/main/cpp/helloworld.cpp") << """
+            #include "greeting.h"
+
+            int main() {
+                greeting();
+                return 0;
+            }
+        """
+
+        when:
+        run "mainExecutable"
+
+        then:
+        def executable = executable("build/binaries/mainExecutable/main")
+        executable.exec().out == "Hello!"
+    }
+}
diff --git a/subprojects/cpp/src/integTest/groovy/org/gradle/nativecode/language/cpp/CppCallingCLanguageIntegrationTest.groovy b/subprojects/cpp/src/integTest/groovy/org/gradle/nativecode/language/cpp/CppCallingCLanguageIntegrationTest.groovy
new file mode 100755
index 0000000..b16d5d9
--- /dev/null
+++ b/subprojects/cpp/src/integTest/groovy/org/gradle/nativecode/language/cpp/CppCallingCLanguageIntegrationTest.groovy
@@ -0,0 +1,25 @@
+/*
+ * Copyright 2013 the original author or authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.gradle.nativecode.language.cpp
+
+import org.gradle.nativecode.language.cpp.fixtures.app.CppCallingCHelloWorldApp
+import org.gradle.nativecode.language.cpp.fixtures.app.HelloWorldApp
+
+class CppCallingCLanguageIntegrationTest extends AbstractLanguageIntegrationTest {
+    HelloWorldApp helloWorldApp = new CppCallingCHelloWorldApp()
+}
+
diff --git a/subprojects/cpp/src/integTest/groovy/org/gradle/nativecode/language/cpp/CppExePluginGoodBehaviourTest.groovy b/subprojects/cpp/src/integTest/groovy/org/gradle/nativecode/language/cpp/CppExePluginGoodBehaviourTest.groovy
new file mode 100644
index 0000000..2d38fc4
--- /dev/null
+++ b/subprojects/cpp/src/integTest/groovy/org/gradle/nativecode/language/cpp/CppExePluginGoodBehaviourTest.groovy
@@ -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.nativecode.language.cpp
+
+import org.gradle.integtests.fixtures.WellBehavedPluginTest
+
+class CppExePluginGoodBehaviourTest extends WellBehavedPluginTest {
+    @Override
+    def String getPluginId() {
+        return "cpp-exe"
+    }
+}
diff --git a/subprojects/cpp/src/integTest/groovy/org/gradle/nativecode/language/cpp/CppExePluginIntegrationTest.groovy b/subprojects/cpp/src/integTest/groovy/org/gradle/nativecode/language/cpp/CppExePluginIntegrationTest.groovy
new file mode 100644
index 0000000..925e0af
--- /dev/null
+++ b/subprojects/cpp/src/integTest/groovy/org/gradle/nativecode/language/cpp/CppExePluginIntegrationTest.groovy
@@ -0,0 +1,65 @@
+/*
+ * Copyright 2013 the original author or authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+
+
+package org.gradle.nativecode.language.cpp
+
+import org.gradle.nativecode.language.cpp.fixtures.AbstractInstalledToolChainIntegrationSpec
+import org.gradle.util.Requires
+import org.gradle.util.TestPrecondition
+
+class CppExePluginIntegrationTest extends AbstractInstalledToolChainIntegrationSpec {
+    @Requires(TestPrecondition.CAN_INSTALL_EXECUTABLE)
+    def "build, install and execute simple c++ application that uses conventional layout"() {
+        given:
+        buildFile << """
+            apply plugin: "cpp-exe"
+        """
+        settingsFile << "rootProject.name = 'test'"
+
+        and:
+        file("src/main/headers/helloworld.h") << """
+            #define MESSAGE "Hello world!"
+        """
+
+        file("src/main/cpp/helloworld.cpp") << """
+            #include <iostream>
+            #include "helloworld.h"
+
+            int main () {
+              std::cout << MESSAGE;
+              return 0;
+            }
+        """
+
+        when:
+        run "mainExecutable"
+
+        then:
+        def executable = executable("build/binaries/mainExecutable/test")
+        executable.assertExists()
+        executable.exec().out == "Hello world!"
+
+        when:
+        run "installMainExecutable"
+
+        then:
+        def installation = installation("build/install/mainExecutable")
+        installation.assertInstalled()
+        installation.exec().out == "Hello world!"
+    }
+}
diff --git a/subprojects/cpp/src/integTest/groovy/org/gradle/nativecode/language/cpp/CppLanguageIncrementalBuildIntegrationTest.groovy b/subprojects/cpp/src/integTest/groovy/org/gradle/nativecode/language/cpp/CppLanguageIncrementalBuildIntegrationTest.groovy
new file mode 100755
index 0000000..7621a6b
--- /dev/null
+++ b/subprojects/cpp/src/integTest/groovy/org/gradle/nativecode/language/cpp/CppLanguageIncrementalBuildIntegrationTest.groovy
@@ -0,0 +1,25 @@
+/*
+ * Copyright 2013 the original author or authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.gradle.nativecode.language.cpp
+import org.gradle.nativecode.language.cpp.fixtures.app.CppHelloWorldApp
+import org.gradle.nativecode.language.cpp.fixtures.app.IncrementalHelloWorldApp
+
+class CppLanguageIncrementalBuildIntegrationTest extends AbstractLanguageIncrementalBuildIntegrationTest {
+    IncrementalHelloWorldApp getHelloWorldApp() {
+        new CppHelloWorldApp()
+    }
+}
diff --git a/subprojects/cpp/src/integTest/groovy/org/gradle/nativecode/language/cpp/CppLanguageIntegrationTest.groovy b/subprojects/cpp/src/integTest/groovy/org/gradle/nativecode/language/cpp/CppLanguageIntegrationTest.groovy
new file mode 100755
index 0000000..3339007
--- /dev/null
+++ b/subprojects/cpp/src/integTest/groovy/org/gradle/nativecode/language/cpp/CppLanguageIntegrationTest.groovy
@@ -0,0 +1,53 @@
+/*
+ * Copyright 2013 the original author or authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.gradle.nativecode.language.cpp
+
+import org.gradle.nativecode.language.cpp.fixtures.app.CppHelloWorldApp
+import org.gradle.nativecode.language.cpp.fixtures.app.HelloWorldApp
+
+class CppLanguageIntegrationTest extends AbstractLanguageIntegrationTest {
+
+    HelloWorldApp helloWorldApp = new CppHelloWorldApp()
+
+    def "build fails when compilation fails"() {
+        given:
+        buildFile << """
+             apply plugin: "cpp"
+             sources {
+                 main {}
+             }
+             executables {
+                 main {
+                     source sources.main
+                 }
+             }
+         """
+
+        and:
+        file("src/main/cpp/broken.cpp") << """
+        #include <iostream>
+
+        'broken
+"""
+
+        expect:
+        fails "mainExecutable"
+        failure.assertHasDescription("Execution failed for task ':compileMainExecutableMainCpp'.");
+        failure.assertHasCause("C++ compiler failed; see the error output for details.")
+    }
+}
+
diff --git a/subprojects/cpp/src/integTest/groovy/org/gradle/nativecode/language/cpp/CppLibPluginGoodBehaviourTest.groovy b/subprojects/cpp/src/integTest/groovy/org/gradle/nativecode/language/cpp/CppLibPluginGoodBehaviourTest.groovy
new file mode 100644
index 0000000..788199a
--- /dev/null
+++ b/subprojects/cpp/src/integTest/groovy/org/gradle/nativecode/language/cpp/CppLibPluginGoodBehaviourTest.groovy
@@ -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.nativecode.language.cpp
+
+import org.gradle.integtests.fixtures.WellBehavedPluginTest
+
+class CppLibPluginGoodBehaviourTest extends WellBehavedPluginTest {
+    @Override
+    def String getPluginId() {
+        return "cpp-lib"
+    }
+}
diff --git a/subprojects/cpp/src/integTest/groovy/org/gradle/nativecode/language/cpp/CppLibPluginIntegrationTest.groovy b/subprojects/cpp/src/integTest/groovy/org/gradle/nativecode/language/cpp/CppLibPluginIntegrationTest.groovy
new file mode 100644
index 0000000..d66c148
--- /dev/null
+++ b/subprojects/cpp/src/integTest/groovy/org/gradle/nativecode/language/cpp/CppLibPluginIntegrationTest.groovy
@@ -0,0 +1,61 @@
+/*
+ * Copyright 2013 the original author or authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.gradle.nativecode.language.cpp
+
+import org.gradle.nativecode.language.cpp.fixtures.AbstractInstalledToolChainIntegrationSpec
+
+class CppLibPluginIntegrationTest extends AbstractInstalledToolChainIntegrationSpec {
+    def "build simple c++ library that uses conventional layout"() {
+        given:
+        buildFile << """
+            apply plugin: "cpp-lib"
+        """
+        settingsFile << "rootProject.name = 'test'"
+
+        and:
+        file("src/main/headers/helloworld.h") << """
+            #ifdef _WIN32
+            #define LIB_FUNC __declspec(dllexport)
+            #else
+            #define LIB_FUNC
+            #endif
+
+            void LIB_FUNC greeting();
+        """
+
+        file("src/main/cpp/helloworld.cpp") << """
+            #include <iostream>
+            #include "helloworld.h"
+
+            void greeting() {
+              std::cout << "Hello world!";
+            }
+        """
+
+        when:
+        run "mainSharedLibrary"
+
+        then:
+        sharedLibrary("build/binaries/mainSharedLibrary/test").assertExists()
+
+        when:
+        run "mainStaticLibrary"
+
+        then:
+        staticLibrary("build/binaries/mainStaticLibrary/test").assertExists()
+    }
+}
diff --git a/subprojects/cpp/src/integTest/groovy/org/gradle/nativecode/language/cpp/CppPluginGoodBehaviourTest.groovy b/subprojects/cpp/src/integTest/groovy/org/gradle/nativecode/language/cpp/CppPluginGoodBehaviourTest.groovy
new file mode 100644
index 0000000..85f44b5
--- /dev/null
+++ b/subprojects/cpp/src/integTest/groovy/org/gradle/nativecode/language/cpp/CppPluginGoodBehaviourTest.groovy
@@ -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.nativecode.language.cpp
+
+import org.gradle.integtests.fixtures.WellBehavedPluginTest
+
+class CppPluginGoodBehaviourTest extends WellBehavedPluginTest {
+    @Override
+    def String getPluginId() {
+        return "cpp"
+    }
+}
diff --git a/subprojects/cpp/src/integTest/groovy/org/gradle/nativecode/language/cpp/CppPluginIntegrationTest.groovy b/subprojects/cpp/src/integTest/groovy/org/gradle/nativecode/language/cpp/CppPluginIntegrationTest.groovy
new file mode 100755
index 0000000..deaded1
--- /dev/null
+++ b/subprojects/cpp/src/integTest/groovy/org/gradle/nativecode/language/cpp/CppPluginIntegrationTest.groovy
@@ -0,0 +1,138 @@
+/*
+ * Copyright 2013 the original author or authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.gradle.nativecode.language.cpp
+
+import org.gradle.nativecode.language.cpp.fixtures.AbstractInstalledToolChainIntegrationSpec
+import org.gradle.nativecode.language.cpp.fixtures.app.CppHelloWorldApp
+
+class CppPluginIntegrationTest extends AbstractInstalledToolChainIntegrationSpec {
+
+    static final HELLO_WORLD = "Hello, World!"
+    static final HELLO_WORLD_FRENCH = "Bonjour, Monde!"
+
+    def "can explicitly configure tool chain in path"() {
+        def helloWorld = new CppHelloWorldApp()
+
+        given:
+        buildFile << """
+            apply plugin: "cpp-exe"
+
+            toolChains {
+                test(${toolChain.getImplementationClass()})
+            }
+"""
+        settingsFile << "rootProject.name = 'test'"
+
+        helloWorld.writeSources(file("src/main"))
+
+        when:
+        run "mainExecutable"
+
+        then:
+        def executable = executable("build/binaries/mainExecutable/test")
+        executable.assertExists()
+        executable.exec().out == helloWorld.englishOutput
+    }
+
+    def "build and execute program with non-conventional source layout"() {
+        given:
+        buildFile << """
+            apply plugin: "cpp"
+            sources {
+                main {
+                    cpp {
+                        source {
+                            srcDirs "src"
+                            include "**/*.cpp"
+                        }
+                        exportedHeaders {
+                            srcDirs "src/hello", "include"
+                        }
+                    }
+                    c {
+                        source {
+                            srcDirs "src", "include"
+                            include "**/*.c"
+                        }
+                        exportedHeaders {
+                            srcDirs "src/hello", "include"
+                        }
+                    }
+                }
+            }
+            executables {
+                main {
+                    source sources.main
+                }
+            }
+        """
+        settingsFile << "rootProject.name = 'test'"
+
+        and:
+        file("src", "hello", "hello.cpp") << """
+            #include <iostream>
+
+            void hello () {
+              std::cout << "${HELLO_WORLD}";
+            }
+        """
+
+        and:
+        file("src", "hello", "french", "bonjour.c") << """
+            #include <stdio.h>
+            #include "otherProject/bonjour.h"
+
+            void bonjour() {
+                printf("${HELLO_WORLD_FRENCH}");
+            }
+        """
+
+        and:
+        file("src", "hello", "hello.h") << """
+            void hello();
+        """
+
+        and:
+        file("src", "app", "main", "main.cpp") << """
+            #include "hello.h"
+            extern "C" {
+                #include "otherProject/bonjour.h"
+            }
+
+            int main () {
+              hello();
+              bonjour();
+              return 0;
+            }
+        """
+
+        and:
+        file("include", "otherProject", "bonjour.h") << """
+            void bonjour();
+        """
+
+        and:
+        file("include", "otherProject", "bonjour.cpp") << """
+            THIS FILE WON'T BE COMPILED
+        """
+
+        when:
+        run "mainExecutable"
+
+        then:
+        executable("build/binaries/mainExecutable/main").exec().out == HELLO_WORLD + HELLO_WORLD_FRENCH
+    }
+}
diff --git a/subprojects/cpp/src/integTest/groovy/org/gradle/nativecode/language/cpp/CppSamplesIntegrationTest.groovy b/subprojects/cpp/src/integTest/groovy/org/gradle/nativecode/language/cpp/CppSamplesIntegrationTest.groovy
new file mode 100755
index 0000000..6a76356
--- /dev/null
+++ b/subprojects/cpp/src/integTest/groovy/org/gradle/nativecode/language/cpp/CppSamplesIntegrationTest.groovy
@@ -0,0 +1,189 @@
+/*
+ * Copyright 2012 the original author or authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.gradle.nativecode.language.cpp
+import org.gradle.integtests.fixtures.Sample
+import org.gradle.nativecode.language.cpp.fixtures.AbstractInstalledToolChainIntegrationSpec
+import org.gradle.util.Requires
+import org.gradle.util.TestPrecondition
+import org.junit.Rule
+
+ at Requires(TestPrecondition.CAN_INSTALL_EXECUTABLE)
+class CppSamplesIntegrationTest extends AbstractInstalledToolChainIntegrationSpec {
+    @Rule public final Sample c = new Sample(temporaryFolder, 'cpp/c')
+    @Rule public final Sample asm = new Sample(temporaryFolder, 'cpp/c-with-assembler')
+    @Rule public final Sample cpp = new Sample(temporaryFolder, 'cpp/cpp')
+    @Rule public final Sample cppExe = new Sample(temporaryFolder, 'cpp/cpp-exe')
+    @Rule public final Sample cppLib = new Sample(temporaryFolder, 'cpp/cpp-lib')
+    @Rule public final Sample multiProject = new Sample(temporaryFolder, 'cpp/multi-project')
+    @Rule public final Sample variants = new Sample(temporaryFolder, 'cpp/variants')
+    @Rule public final Sample dependencies = new Sample(temporaryFolder, 'cpp/dependencies')
+
+    def "asm"() {
+        given:
+        sample asm
+
+        when:
+        run "installMainExecutable"
+
+        then:
+        executedAndNotSkipped ":assembleMainExecutableMainAsm", ":compileMainExecutableMainC", ":linkMainExecutable", ":mainExecutable"
+
+        and:
+        installation("cpp/c-with-assembler/build/install/mainExecutable").exec().out == "5 + 7 = 12\n"
+    }
+
+    def "c"() {
+        given:
+        sample c
+        
+        when:
+        run "installMainExecutable"
+        
+        then:
+        executedAndNotSkipped ":compileHelloSharedLibraryLibC", ":linkHelloSharedLibrary", ":helloSharedLibrary",
+                              ":compileMainExecutableExeC", ":linkMainExecutable", ":mainExecutable"
+
+        and:
+        installation("cpp/c/build/install/mainExecutable").exec().out == "Hello world!"
+    }
+
+    def "cpp"() {
+        given:
+        sample cpp
+
+        when:
+        run "installMainExecutable"
+
+        then:
+        executedAndNotSkipped ":compileHelloSharedLibraryLibCpp", ":linkHelloSharedLibrary", ":helloSharedLibrary",
+                              ":compileMainExecutableExeCpp", ":linkMainExecutable", ":mainExecutable"
+
+        and:
+        installation("cpp/cpp/build/install/mainExecutable").exec().out == "Hello world!\n"
+    }
+
+    def "exe"() {
+        given:
+        sample cppExe
+
+        when:
+        run "installMain"
+
+        then:
+        executedAndNotSkipped ":compileMainExecutableMainCpp", ":linkMainExecutable", ":stripMainExecutable", ":mainExecutable"
+
+        and:
+        executable("cpp/cpp-exe/build/binaries/mainExecutable/sampleExe").exec().out == "Hello, World!\n"
+        installation("cpp/cpp-exe/build/install/mainExecutable").exec().out == "Hello, World!\n"
+    }
+
+    def "lib"() {
+        given:
+        sample cppLib
+        
+        when:
+        run "mainSharedLibrary"
+        
+        then:
+        executedAndNotSkipped ":compileMainSharedLibraryMainCpp", ":linkMainSharedLibrary", ":mainSharedLibrary"
+        
+        and:
+        sharedLibrary("cpp/cpp-lib/build/binaries/mainSharedLibrary/sampleLib").assertExists()
+        
+        when:
+        sample cppLib
+        run "mainStaticLibrary"
+        
+        then:
+        executedAndNotSkipped ":compileMainStaticLibraryMainCpp", ":createMainStaticLibrary", ":mainStaticLibrary"
+        
+        and:
+        staticLibrary("cpp/cpp-lib/build/binaries/mainStaticLibrary/sampleLib").assertExists()
+    }
+
+    def "variants"() {
+        when:
+        sample variants
+        run "installEnglishMainExecutable"
+
+        then:
+        executedAndNotSkipped ":compileEnglishHelloSharedLibraryLibCpp", ":linkEnglishHelloSharedLibrary", ":englishHelloSharedLibrary"
+        executedAndNotSkipped ":compileEnglishMainExecutableExeCpp", ":linkEnglishMainExecutable", ":englishMainExecutable"
+
+        and:
+        executable("cpp/variants/build/binaries/mainExecutable/english/main").assertExists()
+        sharedLibrary("cpp/variants/build/binaries/helloSharedLibrary/english/hello").assertExists()
+
+        and:
+        installation("cpp/variants/build/install/mainExecutable/english").exec().out == "Hello world!\n"
+
+        when:
+        sample variants
+        run "installFrenchMainExecutable"
+
+        then:
+        executedAndNotSkipped ":compileFrenchHelloSharedLibraryLibCpp", ":linkFrenchHelloSharedLibrary", ":frenchHelloSharedLibrary"
+        executedAndNotSkipped ":compileFrenchMainExecutableExeCpp", ":linkFrenchMainExecutable", ":frenchMainExecutable"
+
+        and:
+        executable("cpp/variants/build/binaries/mainExecutable/french/main").assertExists()
+        sharedLibrary("cpp/variants/build/binaries/helloSharedLibrary/french/hello").assertExists()
+
+        and:
+        installation("cpp/variants/build/install/mainExecutable/french").exec().out == "Bonjour monde!\n"
+    }
+
+    def multiProject() {
+        given:
+        sample multiProject
+
+        when:
+        run "installMainExecutable"
+
+        then:
+        ":exe:mainExecutable" in executedTasks
+
+        and:
+        sharedLibrary("cpp/multi-project/lib/build/binaries/mainSharedLibrary/lib").assertExists()
+        executable("cpp/multi-project/exe/build/binaries/mainExecutable/exe").assertExists()
+        installation("cpp/multi-project/exe/build/install/mainExecutable").exec().out == "Hello, World!\n"
+    }
+
+    // Does not work on windows, due to GRADLE-2118
+    @Requires(TestPrecondition.NOT_WINDOWS)
+    def "dependencies"() {
+        when:
+        sample dependencies
+        run ":lib:uploadArchives"
+
+        then:
+        sharedLibrary("cpp/dependencies/lib/build/binaries/mainSharedLibrary/lib").assertExists()
+        file("cpp/dependencies/lib/build/repo/some-org/some-lib/1.0/some-lib-1.0-so.so").isFile()
+
+        when:
+        sample dependencies
+        run ":exe:uploadArchives"
+
+        then:
+        ":exe:mainCppExtractHeaders" in nonSkippedTasks
+        ":exe:mainExecutable" in nonSkippedTasks
+
+        and:
+        executable("cpp/dependencies/exe/build/binaries/mainExecutable/exe").assertExists()
+        file("cpp/dependencies/exe/build/repo/dependencies/exe/1.0/exe-1.0.exe").exists()
+    }
+
+}
\ No newline at end of file
diff --git a/subprojects/cpp/src/integTest/groovy/org/gradle/nativecode/language/cpp/MixedLanguageIntegrationTest.groovy b/subprojects/cpp/src/integTest/groovy/org/gradle/nativecode/language/cpp/MixedLanguageIntegrationTest.groovy
new file mode 100755
index 0000000..d3360cb
--- /dev/null
+++ b/subprojects/cpp/src/integTest/groovy/org/gradle/nativecode/language/cpp/MixedLanguageIntegrationTest.groovy
@@ -0,0 +1,81 @@
+/*
+ * Copyright 2013 the original author or authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.gradle.nativecode.language.cpp
+
+import org.gradle.nativecode.language.cpp.fixtures.app.HelloWorldApp
+import org.gradle.nativecode.language.cpp.fixtures.app.MixedLanguageHelloWorldApp
+import org.gradle.nativecode.language.cpp.fixtures.app.SourceFile
+
+class MixedLanguageIntegrationTest extends AbstractLanguageIntegrationTest {
+
+    HelloWorldApp helloWorldApp = new MixedLanguageHelloWorldApp(toolChain)
+
+    def "can have all source files co-located in a common directory"() {
+        given:
+        buildFile << """
+            apply plugin: "cpp"
+            sources {
+                main {
+                    cpp {
+                        source {
+                            srcDirs "src/main/flat"
+                            include "**/*.cpp"
+                        }
+                    }
+                    c {
+                        source {
+                            srcDirs "src/main/flat"
+                            include "**/*.c"
+                        }
+                        exportedHeaders {
+                            srcDirs "src/main/flat"
+                        }
+                    }
+                    asm {
+                        source {
+                            srcDirs "src/main/flat"
+                            include "**/*.s"
+                        }
+                    }
+                }
+            }
+            executables {
+                main {
+                    source sources.main
+                }
+            }
+            binaries.all {
+                $helloWorldApp.customArgs
+            }
+        """
+
+        and:
+        helloWorldApp.sourceFiles.each { SourceFile sourceFile ->
+            file("src/main/flat/${sourceFile.name}") << sourceFile.content
+        }
+
+        when:
+        run "mainExecutable"
+
+        then:
+        def mainExecutable = executable("build/binaries/mainExecutable/main")
+        mainExecutable.assertExists()
+        mainExecutable.exec().out == helloWorldApp.englishOutput
+    }
+
+}
+
diff --git a/subprojects/cpp/src/integTest/groovy/org/gradle/nativecode/language/cpp/MultipleToolChainIntegrationTest.groovy b/subprojects/cpp/src/integTest/groovy/org/gradle/nativecode/language/cpp/MultipleToolChainIntegrationTest.groovy
new file mode 100755
index 0000000..8833248
--- /dev/null
+++ b/subprojects/cpp/src/integTest/groovy/org/gradle/nativecode/language/cpp/MultipleToolChainIntegrationTest.groovy
@@ -0,0 +1,116 @@
+/*
+ * Copyright 2013 the original author or authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+
+package org.gradle.nativecode.language.cpp
+
+import org.gradle.integtests.fixtures.AbstractIntegrationSpec
+import org.gradle.internal.os.OperatingSystem
+import org.gradle.nativecode.language.cpp.fixtures.AvailableToolChains
+import org.gradle.nativecode.language.cpp.fixtures.app.CppHelloWorldApp
+import org.gradle.util.Requires
+import org.gradle.util.TestPrecondition
+
+class MultipleToolChainIntegrationTest extends AbstractIntegrationSpec {
+    def helloWorld = new CppHelloWorldApp()
+
+    @Requires(TestPrecondition.CAN_INSTALL_EXECUTABLE)
+    def "can build with all available tool chains"() {
+        List<AvailableToolChains.InstalledToolChain> installedToolChains = []
+        for (AvailableToolChains.ToolChainCandidate toolChainCandidate : AvailableToolChains.getToolChains()) {
+            if (toolChainCandidate.isAvailable()) {
+                installedToolChains << toolChainCandidate
+            }
+        }
+
+        def toolChainConfig = installedToolChains.collect({it.buildScriptConfig}).join("\n")
+
+        when:
+        buildFile << """
+            apply plugin: "cpp"
+
+            toolChains {
+${toolChainConfig}
+
+                unavailable(Gcc) {
+                    linker.exe = "does_not_exist"
+                }
+            }
+
+            sources {
+                main {}
+            }
+            executables {
+                main {
+                    source sources.main
+                }
+            }
+"""
+
+        helloWorld.writeSources(file("src/main"))
+
+        then:
+        def tasks = installedToolChains.collect { "install${it.id.capitalize()}MainExecutable" }
+        succeeds tasks as String[]
+
+        and:
+        installedToolChains.each { toolChain ->
+            checkInstall("build/install/mainExecutable/${toolChain.id}/main", toolChain.runtimeEnv)
+        }
+    }
+
+    @Requires(TestPrecondition.CAN_INSTALL_EXECUTABLE)
+    def "includes tool chain in task names and binary paths with two defined and one available"() {
+        AvailableToolChains.InstalledToolChain toolChain = AvailableToolChains.getToolChains().get(0) as AvailableToolChains.InstalledToolChain
+
+        given:
+        buildFile << """
+            apply plugin: "cpp"
+
+            toolChains {
+${toolChain.buildScriptConfig}
+
+                unavailable(Gcc) {
+                    linker.exe = "does_not_exist"
+                }
+            }
+
+            sources {
+                main {}
+            }
+            executables {
+                main {
+                    source sources.main
+                }
+            }
+"""
+
+        helloWorld.writeSources(file("src/main"))
+
+        when:
+        succeeds "install${toolChain.id.capitalize()}MainExecutable"
+
+        then:
+        checkInstall("build/install/mainExecutable/${toolChain.id}/main", toolChain.runtimeEnv)
+    }
+
+    def checkInstall(String path, List runtimeEnv) {
+        def executable = file(OperatingSystem.current().getScriptName(path))
+        executable.assertExists()
+        assert executable.execute([], runtimeEnv).out == helloWorld.englishOutput
+        return true
+    }
+}
diff --git a/subprojects/cpp/src/integTest/groovy/org/gradle/nativecode/language/cpp/NativeBinariesPluginIntegrationTest.groovy b/subprojects/cpp/src/integTest/groovy/org/gradle/nativecode/language/cpp/NativeBinariesPluginIntegrationTest.groovy
new file mode 100755
index 0000000..907a68b
--- /dev/null
+++ b/subprojects/cpp/src/integTest/groovy/org/gradle/nativecode/language/cpp/NativeBinariesPluginIntegrationTest.groovy
@@ -0,0 +1,222 @@
+/*
+ * Copyright 2012 the original author or authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.gradle.nativecode.language.cpp
+
+import org.gradle.nativecode.language.cpp.fixtures.AbstractInstalledToolChainIntegrationSpec
+import org.gradle.nativecode.language.cpp.fixtures.app.CppCallingCHelloWorldApp
+
+class NativeBinariesPluginIntegrationTest extends AbstractInstalledToolChainIntegrationSpec {
+    def helloWorldApp = new CppCallingCHelloWorldApp()
+
+    def "setup"() {
+        settingsFile << "rootProject.name = 'test'"
+    }
+
+    def "skips building executable binary with no source"() {
+        given:
+        buildFile << """
+            apply plugin: "cpp"
+            executables {
+                main {}
+            }
+        """
+
+        when:
+        succeeds "mainExecutable"
+
+        then:
+        executable("build/binaries/mainExecutable/main").assertDoesNotExist()
+    }
+
+    def "assemble executable from component with multiple language source sets"() {
+        given:
+        useMixedSources()
+
+        when:
+        buildFile << """
+            apply plugin: "cpp"
+            sources {
+                main {}
+            }
+            executables {
+                main {
+                    source sources.main.cpp
+                    source sources.main.c
+                }
+            }
+        """
+
+        then:
+        succeeds "mainExecutable"
+
+        and:
+        executable("build/binaries/mainExecutable/main").exec().out == helloWorldApp.englishOutput
+    }
+
+    def "assemble executable binary directly from language source sets"() {
+        given:
+        useMixedSources()
+
+        when:
+        buildFile << """
+            apply plugin: "cpp"
+            sources {
+                main {}
+            }
+            executables {
+                main {}
+            }
+            binaries.all {
+                source sources.main.cpp
+                source sources.main.c
+            }
+        """
+
+        then:
+        succeeds "mainExecutable"
+
+        and:
+        executable("build/binaries/mainExecutable/main").exec().out == helloWorldApp.englishOutput
+    }
+
+    def "assemble executable binary directly from functional source set"() {
+        given:
+        useMixedSources()
+
+        when:
+        buildFile << """
+            apply plugin: "cpp"
+            sources {
+                main {}
+            }
+            executables {
+                main {}
+            }
+            binaries.all {
+                source sources.main
+            }
+        """
+        
+        then:
+        succeeds "mainExecutable"
+
+        and:
+        executable("build/binaries/mainExecutable/main").exec().out == helloWorldApp.englishOutput
+    }
+
+    def "ignores java sources added to binary"() {
+        given:
+        useMixedSources()
+        file("src/main/java/HelloWorld.java") << """
+    This would not compile
+"""
+
+        when:
+        buildFile << """
+            apply plugin: "cpp"
+            apply plugin: "java"
+            sources {
+                main {}
+            }
+            executables {
+                main {
+                    source sources.main.cpp
+                    source sources.main.c
+                    source sources.main.java
+                }
+            }
+         """
+
+        then:
+        succeeds "mainExecutable"
+
+        and:
+        executable("build/binaries/mainExecutable/main").exec().out == helloWorldApp.englishOutput
+    }
+
+    private def useMixedSources() {
+        helloWorldApp.writeSources(file("src/main"))
+    }
+
+    def "build fails when link executable fails"() {
+        given:
+        buildFile << """
+            apply plugin: "cpp-exe"
+        """
+
+        and:
+        file("src", "main", "cpp", "helloworld.cpp") << """
+            int thing() { return 0; }
+        """
+
+        expect:
+        fails "mainExecutable"
+        failure.assertHasDescription("Execution failed for task ':linkMainExecutable'.");
+        failure.assertHasCause("Linker failed; see the error output for details.")
+    }
+
+    def "build fails when link library fails"() {
+        given:
+        buildFile << """
+            apply plugin: "cpp-lib"
+            binaries.all {
+                linkerArgs "--not-an-option"
+            }
+        """
+
+        and:
+        file("src/main/cpp/hello.cpp") << """
+            #include "test.h"
+            void hello() {
+                test();
+            }
+"""
+        // Header file available, but no implementation to link
+        file("src/main/cpp/test.h") << """
+            int test();
+"""
+
+        when:
+        fails "mainSharedLibrary"
+
+        then:
+        failure.assertHasDescription("Execution failed for task ':linkMainSharedLibrary'.");
+        failure.assertHasCause("Linker failed; see the error output for details.")
+    }
+
+    def "build fails when create static library fails"() {
+        given:
+        buildFile << """
+            apply plugin: "cpp-lib"
+            binaries.withType(StaticLibraryBinary) {
+                staticLibArgs "not_a_file"
+            }
+        """
+
+        and:
+        file("src/main/cpp/hello.cpp") << """
+            void hello() {
+            }
+"""
+
+        when:
+        fails "mainStaticLibrary"
+
+        then:
+        failure.assertHasDescription("Execution failed for task ':createMainStaticLibrary'.");
+        failure.assertHasCause("Static library archiver failed; see the error output for details.")
+    }
+}
diff --git a/subprojects/cpp/src/integTest/groovy/org/gradle/nativecode/language/cpp/fixtures/AbstractInstalledToolChainIntegrationSpec.groovy b/subprojects/cpp/src/integTest/groovy/org/gradle/nativecode/language/cpp/fixtures/AbstractInstalledToolChainIntegrationSpec.groovy
new file mode 100755
index 0000000..7f181ce
--- /dev/null
+++ b/subprojects/cpp/src/integTest/groovy/org/gradle/nativecode/language/cpp/fixtures/AbstractInstalledToolChainIntegrationSpec.groovy
@@ -0,0 +1,51 @@
+/*
+ * Copyright 2012 the original author or authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.gradle.nativecode.language.cpp.fixtures
+
+import org.gradle.integtests.fixtures.AbstractIntegrationSpec
+import org.gradle.test.fixtures.file.TestFile
+import org.junit.runner.RunWith
+/**
+ * Runs a test separately for each installed tool chain.
+ */
+ at RunWith(SingleToolChainTestRunner.class)
+abstract class AbstractInstalledToolChainIntegrationSpec extends AbstractIntegrationSpec {
+    static AvailableToolChains.InstalledToolChain toolChain
+
+    def NativeInstallationFixture installation(Object installDir) {
+        return new NativeInstallationFixture(file(installDir))
+    }
+
+    def ExecutableFixture executable(Object path) {
+        return toolChain.executable(file(path))
+    }
+
+    def TestFile objectFile(Object path) {
+        if (toolChain.visualCpp) {
+            return file("${path}.obj")
+        }
+        return file("${path}.o")
+    }
+
+    def SharedLibraryFixture sharedLibrary(Object path) {
+        return toolChain.sharedLibrary(file(path))
+    }
+
+    def NativeBinaryFixture staticLibrary(Object path) {
+        return toolChain.staticLibrary(file(path))
+    }
+}
diff --git a/subprojects/cpp/src/integTest/groovy/org/gradle/nativecode/language/cpp/fixtures/AvailableToolChains.java b/subprojects/cpp/src/integTest/groovy/org/gradle/nativecode/language/cpp/fixtures/AvailableToolChains.java
new file mode 100755
index 0000000..7bbe71b
--- /dev/null
+++ b/subprojects/cpp/src/integTest/groovy/org/gradle/nativecode/language/cpp/fixtures/AvailableToolChains.java
@@ -0,0 +1,312 @@
+/*
+ * Copyright 2012 the original author or authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.gradle.nativecode.language.cpp.fixtures;
+
+import com.google.common.base.Joiner;
+import org.gradle.internal.nativeplatform.ProcessEnvironment;
+import org.gradle.internal.nativeplatform.services.NativeServices;
+import org.gradle.internal.os.OperatingSystem;
+import org.gradle.nativecode.toolchain.Gcc;
+import org.gradle.nativecode.toolchain.VisualCpp;
+import org.gradle.nativecode.toolchain.internal.gpp.version.GppVersionDeterminer;
+import org.gradle.nativecode.toolchain.internal.msvcpp.VisualStudioInstall;
+import org.gradle.test.fixtures.file.TestFile;
+import org.gradle.util.TextUtil;
+
+import java.io.File;
+import java.util.*;
+
+public class AvailableToolChains {
+    /**
+     * @return A list of all tool chains for this platform, with the default tool chain listed first.
+     */
+    public static List<ToolChainCandidate> getToolChains() {
+        List<ToolChainCandidate> compilers = new ArrayList<ToolChainCandidate>();
+        if (OperatingSystem.current().isWindows()) {
+            compilers.add(findVisualCpp());
+            compilers.add(findMinGW());
+            compilers.add(findCygwin());
+        } else {
+            // GCC4.x must be on the path
+            compilers.add(findGpp("4", null));
+            // CI servers have GCC4.4 installed additionally
+            compilers.add(findGpp("4.4", "/opt/gcc/4.4/bin/g++"));
+
+            // It's easy to get GCC4.8 installed on OSX, and symlink to this location
+            // Not available on CI servers, so only add it if it's available
+            ToolChainCandidate gpp48 = findGpp("4.8", "/opt/gcc/4.8/bin/g++");
+            if (gpp48.isAvailable()) {
+                compilers.add(gpp48);
+            }
+
+            // TODO:DAZ Make a GCC3 install available for testing
+        }
+        return compilers;
+    }
+
+    static private ToolChainCandidate findVisualCpp() {
+        // Search first in path, then in the standard installation locations
+        File compilerExe = OperatingSystem.current().findInPath("cl.exe");
+        if (compilerExe != null) {
+            return new InstalledVisualCpp("visual c++");
+        }
+
+        VisualStudioInstall install = new VisualStudioInstall(new File("C:/Program Files (x86)/Microsoft Visual Studio 10.0"));
+        if (!install.isInstalled()) {
+            install = new VisualStudioInstall(new File("C:/Program Files/Microsoft Visual Studio 10.0"));
+        }
+
+        if (install.isInstalled()) {
+            return new InstalledVisualCpp("visual c++").withInstall(install);
+        }
+
+        return new UnavailableToolChain("visual c++");
+    }
+
+    static private ToolChainCandidate findMinGW() {
+        // Search in the standard installation locations
+        File compilerExe = new File("C:/MinGW/bin/g++.exe");
+        if (compilerExe.isFile()) {
+            return new InstalledGcc("mingw").inPath(compilerExe.getParentFile());
+        }
+
+        return new UnavailableToolChain("mingw");
+    }
+
+    static private ToolChainCandidate findCygwin() {
+        // Search in the standard installation locations
+        File compilerExe = new File("C:/cygwin/bin/g++.exe");
+        if (compilerExe.isFile()) {
+            return new InstalledGcc("g++ cygwin").inPath(compilerExe.getParentFile());
+        }
+
+        return new UnavailableToolChain("g++ cygwin");
+    }
+
+    static private ToolChainCandidate findGpp(String versionPrefix, String hardcodedFallback) {
+        String name = String.format("g++ %s", versionPrefix);
+        GppVersionDeterminer versionDeterminer = new GppVersionDeterminer();
+
+        List<File> gppCandidates = OperatingSystem.current().findAllInPath("g++");
+        for (int i = 0; i < gppCandidates.size(); i++) {
+            File candidate = gppCandidates.get(i);
+            if (versionDeterminer.transform(candidate).startsWith(versionPrefix)) {
+                InstalledGcc gcc = new InstalledGcc(name);
+                if (i > 0) {
+                    // Not the first g++ in the path, needs the path variable updated
+                    gcc.inPath(candidate.getParentFile());
+                }
+                return gcc;
+            }
+        }
+
+        if (hardcodedFallback != null) {
+            File fallback = new File(hardcodedFallback);
+            if (fallback.isFile()) {
+                return new InstalledGcc(name).inPath(fallback.getParentFile());
+            }
+        }
+
+        return new UnavailableToolChain(name);
+    }
+
+    public static abstract class ToolChainCandidate {
+        @Override
+        public String toString() {
+            return getDisplayName();
+        }
+
+        public abstract String getDisplayName();
+        
+        public abstract boolean isAvailable();
+
+        public abstract void initialiseEnvironment();
+
+        public abstract void resetEnvironment();
+
+        public ExecutableFixture executable(Object path) {
+            return new ExecutableFixture(new TestFile(OperatingSystem.current().getExecutableName(path.toString())), this);
+        }
+
+        public SharedLibraryFixture sharedLibrary(Object path) {
+            return new SharedLibraryFixture(new TestFile(OperatingSystem.current().getSharedLibraryName(path.toString())), this);
+        }
+
+        public NativeBinaryFixture staticLibrary(Object path) {
+            return new NativeBinaryFixture(new TestFile(OperatingSystem.current().getStaticLibraryName(path.toString())), this);
+        }
+    }
+    
+    public abstract static class InstalledToolChain extends ToolChainCandidate {
+        private static final ProcessEnvironment PROCESS_ENVIRONMENT = NativeServices.getInstance().get(ProcessEnvironment.class);
+        protected final List<File> pathEntries = new ArrayList<File>();
+        protected final Map<String, String> environmentVars = new HashMap<String, String>();
+        private final String name;
+        private final String pathVarName;
+        private String originalPath;
+
+        public InstalledToolChain(String name) {
+            this.name = name;
+            this.pathVarName = OperatingSystem.current().getPathVar();
+        }
+
+        InstalledToolChain inPath(File... pathEntries) {
+            Collections.addAll(this.pathEntries, pathEntries);
+            return this;
+        }
+
+        @Override
+        public String getDisplayName() {
+            return name;
+        }
+
+        @Override
+        public boolean isAvailable() {
+            return true;
+        }
+
+        public void initialiseEnvironment() {
+            String compilerPath = Joiner.on(File.pathSeparator).join(pathEntries);
+
+            if (compilerPath.length() > 0) {
+                originalPath = System.getenv(pathVarName);
+                String path = compilerPath + File.pathSeparator + originalPath;
+                System.out.println(String.format("Using path %s", path));
+                PROCESS_ENVIRONMENT.setEnvironmentVariable(pathVarName, path);
+            }
+
+            for (Map.Entry<String, String> entry : environmentVars.entrySet()) {
+                System.out.println(String.format("Using environment var %s -> %s", entry.getKey(), entry.getValue()));
+                PROCESS_ENVIRONMENT.setEnvironmentVariable(entry.getKey(), entry.getValue());
+            }
+        }
+
+        public void resetEnvironment() {
+            if (originalPath != null) {
+                PROCESS_ENVIRONMENT.setEnvironmentVariable(pathVarName, originalPath);
+            }
+        }
+
+        public abstract String getBuildScriptConfig();
+
+        public abstract String getImplementationClass();
+
+        public boolean isVisualCpp() {
+            return false;
+        }
+
+        public List<File> getPathEntries() {
+            return pathEntries;
+        }
+
+        /**
+         * The environment required to execute a binary created by this toolchain.
+         */
+        // TODO:DAZ This isn't quite right (only required for MinGW and cygwin, and preferably not even those)
+        public List<String> getRuntimeEnv() {
+            if (pathEntries.isEmpty()) {
+                return Collections.emptyList();
+            }
+
+            String path = Joiner.on(File.pathSeparator).join(pathEntries) + File.pathSeparator + System.getenv(pathVarName);
+            return Collections.singletonList(pathVarName + "=" + path);
+        }
+
+        public String getId() {
+            return name.replaceAll("\\W", "");
+        }
+    }
+
+    public static class InstalledGcc extends InstalledToolChain {
+        public InstalledGcc(String name) {
+            super(name);
+        }
+
+        @Override
+        public String getBuildScriptConfig() {
+            String config = String.format("%s(%s)\n", getId(), getImplementationClass());
+            for (File pathEntry : getPathEntries()) {
+                config += String.format("%s.path file('%s')", getId(), TextUtil.normaliseFileSeparators(pathEntry.getAbsolutePath()));
+            }
+            return config;
+        }
+
+        public String getImplementationClass() {
+            return Gcc.class.getSimpleName();
+        }
+    }
+
+    public static class InstalledVisualCpp extends InstalledToolChain {
+        private File installDir;
+
+        public InstalledVisualCpp(String name) {
+            super(name);
+        }
+
+        public InstalledVisualCpp withInstall(VisualStudioInstall install) {
+            pathEntries.addAll(install.getPathEntries());
+            environmentVars.putAll(install.getEnvironment());
+            installDir = install.getInstallDir();
+            return this;
+        }
+
+        @Override
+        public String getBuildScriptConfig() {
+            String config = String.format("%s(%s)\n", getId(), getImplementationClass());
+            if (installDir != null) {
+                config += String.format("%s.installDir = file('%s')", getId(), TextUtil.normaliseFileSeparators(installDir.getAbsolutePath()));
+            }
+            return config;
+        }
+
+        public String getImplementationClass() {
+            return VisualCpp.class.getSimpleName();
+        }
+
+        public boolean isVisualCpp() {
+            return true;
+        }
+    }
+
+    public static class UnavailableToolChain extends ToolChainCandidate {
+        private final String name;
+
+        public UnavailableToolChain(String name) {
+            this.name = name;
+        }
+
+        @Override
+        public String getDisplayName() {
+            return name;
+        }
+
+        @Override
+        public boolean isAvailable() {
+            return false;
+        }
+
+        @Override
+        public void initialiseEnvironment() {
+            throw new UnsupportedOperationException("Toolchain is not available");
+        }
+
+        @Override
+        public void resetEnvironment() {
+            throw new UnsupportedOperationException("Toolchain is not available");
+        }
+    }
+}
diff --git a/subprojects/cpp/src/integTest/groovy/org/gradle/nativecode/language/cpp/fixtures/ExecutableFixture.groovy b/subprojects/cpp/src/integTest/groovy/org/gradle/nativecode/language/cpp/fixtures/ExecutableFixture.groovy
new file mode 100644
index 0000000..039214a
--- /dev/null
+++ b/subprojects/cpp/src/integTest/groovy/org/gradle/nativecode/language/cpp/fixtures/ExecutableFixture.groovy
@@ -0,0 +1,31 @@
+/*
+ * Copyright 2013 the original author or authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.gradle.nativecode.language.cpp.fixtures
+import org.gradle.nativecode.language.cpp.fixtures.AvailableToolChains.ToolChainCandidate
+import org.gradle.test.fixtures.file.ExecOutput
+import org.gradle.test.fixtures.file.TestFile
+
+class ExecutableFixture extends NativeBinaryFixture {
+    ExecutableFixture(TestFile file, ToolChainCandidate toolChain) {
+        super(file, toolChain)
+    }
+
+    public ExecOutput exec(Object... args) {
+        assertExists()
+        return file.exec(args)
+    }
+}
diff --git a/subprojects/cpp/src/integTest/groovy/org/gradle/nativecode/language/cpp/fixtures/NativeBinaryFixture.groovy b/subprojects/cpp/src/integTest/groovy/org/gradle/nativecode/language/cpp/fixtures/NativeBinaryFixture.groovy
new file mode 100644
index 0000000..5238f7b
--- /dev/null
+++ b/subprojects/cpp/src/integTest/groovy/org/gradle/nativecode/language/cpp/fixtures/NativeBinaryFixture.groovy
@@ -0,0 +1,67 @@
+/*
+ * Copyright 2013 the original author or authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.gradle.nativecode.language.cpp.fixtures
+
+import org.gradle.test.fixtures.file.TestFile
+
+class NativeBinaryFixture {
+    protected final TestFile file
+    protected final AvailableToolChains.ToolChainCandidate toolChain
+
+    NativeBinaryFixture(TestFile file, AvailableToolChains.ToolChainCandidate toolChain) {
+        this.file = file
+        this.toolChain = toolChain
+    }
+
+    URI toURI() {
+        file.toURI()
+    }
+
+    TestFile.Snapshot snapshot() {
+        file.snapshot()
+    }
+
+    void assertHasChangedSince(TestFile.Snapshot snapshot) {
+        file.assertHasChangedSince(snapshot)
+    }
+
+    void assertExists() {
+        file.assertIsFile()
+    }
+
+    void assertDoesNotExist() {
+        file.assertDoesNotExist()
+    }
+
+    // Does nothing when tool chain does not generate a separate debug file
+    void assertDebugFileExists() {
+        if (toolChain.visualCpp) {
+            getDebugFile().assertIsFile()
+        }
+    }
+
+    // Does nothing when tool chain does not generate a separate debug file
+    void assertDebugFileDoesNotExist() {
+        if (toolChain.visualCpp) {
+            getDebugFile().assertDoesNotExist()
+        }
+    }
+
+    private TestFile getDebugFile() {
+        return file.withExtension("pdb")
+    }
+}
diff --git a/subprojects/cpp/src/integTest/groovy/org/gradle/nativecode/language/cpp/fixtures/NativeInstallationFixture.groovy b/subprojects/cpp/src/integTest/groovy/org/gradle/nativecode/language/cpp/fixtures/NativeInstallationFixture.groovy
new file mode 100644
index 0000000..97adecc
--- /dev/null
+++ b/subprojects/cpp/src/integTest/groovy/org/gradle/nativecode/language/cpp/fixtures/NativeInstallationFixture.groovy
@@ -0,0 +1,73 @@
+/*
+ * Copyright 2013 the original author or authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.gradle.nativecode.language.cpp.fixtures
+
+import org.gradle.internal.os.OperatingSystem
+import org.gradle.test.fixtures.file.ExecOutput
+import org.gradle.test.fixtures.file.TestFile
+
+class NativeInstallationFixture {
+    private final TestFile installDir
+    private final OperatingSystem os = OperatingSystem.current()
+
+    NativeInstallationFixture(TestFile installDir) {
+        this.installDir = installDir
+    }
+
+    ExecOutput exec(Object... args) {
+        assertInstalled()
+        return scriptFile().exec(args)
+    }
+
+    private TestFile scriptFile() {
+        if (os.windows) {
+            return installDir.listFiles().find { it.file && it.name.endsWith(".bat") }
+        } else {
+            return installDir.listFiles().find { it.file }
+        }
+    }
+
+    NativeInstallationFixture assertInstalled() {
+        installDir.assertIsDir()
+        final script = scriptFile()
+        assert script
+
+        def libDir = installDir.file("lib")
+        libDir.assertIsDir()
+        libDir.file(os.getExecutableName(script.name)).assertIsFile()
+        this
+    }
+
+    NativeInstallationFixture assertIncludesLibraries(String... names) {
+        def expected = names.collect { os.getSharedLibraryName(it) } as Set
+        assert libraryFiles.collect { it.name } as Set == expected as Set
+        this
+    }
+
+    private ArrayList<TestFile> getLibraryFiles() {
+        installDir.assertIsDir()
+        def libDir = installDir.file("lib")
+        libDir.assertIsDir()
+        def libFiles
+        if (os.windows) {
+            libFiles = libDir.listFiles().findAll { it.file && !it.name.endsWith(".exe") }
+        } else {
+            libFiles = libDir.listFiles().findAll { it.file && it.name.contains(".") }
+        }
+        libFiles
+    }
+}
diff --git a/subprojects/cpp/src/integTest/groovy/org/gradle/nativecode/language/cpp/fixtures/SharedLibraryFixture.groovy b/subprojects/cpp/src/integTest/groovy/org/gradle/nativecode/language/cpp/fixtures/SharedLibraryFixture.groovy
new file mode 100644
index 0000000..d197bdc
--- /dev/null
+++ b/subprojects/cpp/src/integTest/groovy/org/gradle/nativecode/language/cpp/fixtures/SharedLibraryFixture.groovy
@@ -0,0 +1,43 @@
+/*
+ * Copyright 2013 the original author or authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.gradle.nativecode.language.cpp.fixtures
+
+import org.gradle.test.fixtures.file.TestFile
+
+class SharedLibraryFixture extends NativeBinaryFixture {
+    SharedLibraryFixture(TestFile file, AvailableToolChains.ToolChainCandidate toolChain) {
+        super(file, toolChain)
+    }
+
+    @Override
+    void assertExists() {
+        super.assertExists()
+        if (toolChain.visualCpp) {
+            file.withExtension("lib").assertIsFile()
+            file.withExtension("exp").assertIsFile()
+        }
+    }
+
+    @Override
+    void assertDoesNotExist() {
+        super.assertDoesNotExist()
+        if (toolChain.visualCpp) {
+            file.withExtension("lib").assertDoesNotExist()
+            file.withExtension("exp").assertDoesNotExist()
+        }
+    }
+}
diff --git a/subprojects/cpp/src/integTest/groovy/org/gradle/nativecode/language/cpp/fixtures/SingleToolChainTestRunner.java b/subprojects/cpp/src/integTest/groovy/org/gradle/nativecode/language/cpp/fixtures/SingleToolChainTestRunner.java
new file mode 100755
index 0000000..e7deca2
--- /dev/null
+++ b/subprojects/cpp/src/integTest/groovy/org/gradle/nativecode/language/cpp/fixtures/SingleToolChainTestRunner.java
@@ -0,0 +1,76 @@
+/*
+ * Copyright 2012 the original author or authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.gradle.nativecode.language.cpp.fixtures;
+
+import org.gradle.integtests.fixtures.AbstractMultiTestRunner;
+
+import java.util.List;
+
+public class SingleToolChainTestRunner extends AbstractMultiTestRunner {
+    private static final String TOOLCHAINS_SYSPROP_NAME = "org.gradle.integtest.cpp.toolChains";
+
+    public SingleToolChainTestRunner(Class<? extends AbstractInstalledToolChainIntegrationSpec> target) {
+        super(target);
+    }
+
+    @Override
+    protected void createExecutions() {
+        boolean enableAllToolChains = "all".equals(System.getProperty(TOOLCHAINS_SYSPROP_NAME, "default"));
+        List<AvailableToolChains.ToolChainCandidate> toolChains = AvailableToolChains.getToolChains();
+        for (int i = 0; i < toolChains.size(); i++) {
+            boolean enabled = enableAllToolChains || i == 0;
+            add(new ToolChainExecution(toolChains.get(i), enabled));
+        }
+    }
+
+    private static class ToolChainExecution extends Execution {
+        private final AvailableToolChains.ToolChainCandidate toolChain;
+        private final boolean enabled;
+
+        public ToolChainExecution(AvailableToolChains.ToolChainCandidate toolChain, boolean enabled) {
+            this.toolChain = toolChain;
+            this.enabled = enabled;
+        }
+
+        @Override
+        protected String getDisplayName() {
+            return toolChain.getDisplayName();
+        }
+
+        @Override
+        protected boolean isTestEnabled(TestDetails testDetails) {
+            return enabled;
+        }
+
+        @Override
+        protected void assertCanExecute() {
+            assert toolChain.isAvailable() : String.format("Tool chain %s not available", toolChain.getDisplayName());
+        }
+
+        @Override
+        protected void before() {
+            System.out.println(String.format("Using tool chain %s", toolChain.getDisplayName()));
+            toolChain.initialiseEnvironment();
+            AbstractInstalledToolChainIntegrationSpec.setToolChain((AvailableToolChains.InstalledToolChain) toolChain);
+        }
+
+        @Override
+        protected void after() {
+            toolChain.resetEnvironment();
+        }
+    }
+}
diff --git a/subprojects/cpp/src/integTest/groovy/org/gradle/nativecode/language/cpp/fixtures/app/CCallingCppHelloWorldApp.groovy b/subprojects/cpp/src/integTest/groovy/org/gradle/nativecode/language/cpp/fixtures/app/CCallingCppHelloWorldApp.groovy
new file mode 100644
index 0000000..0a01b3f
--- /dev/null
+++ b/subprojects/cpp/src/integTest/groovy/org/gradle/nativecode/language/cpp/fixtures/app/CCallingCppHelloWorldApp.groovy
@@ -0,0 +1,75 @@
+/*
+ * Copyright 2013 the original author or authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.gradle.nativecode.language.cpp.fixtures.app;
+
+public class CCallingCppHelloWorldApp extends HelloWorldApp {
+    @Override
+    SourceFile getMainSource() {
+        sourceFile("c", "main.c", """
+                #include <stdio.h>
+                #include "hello.h"
+
+                int main () {
+                    sayHello();
+                    printf("%d", sum(5, 7));
+                    return 0;
+                }
+        """)
+    }
+
+    @Override
+    SourceFile getLibraryHeader() {
+        sourceFile("headers", "hello.h", """
+                #ifdef _WIN32
+                #define DLL_FUNC __declspec(dllexport)
+                #else
+                #define DLL_FUNC
+                #endif
+
+                #ifdef __cplusplus
+                extern "C" {
+                #endif
+
+                void DLL_FUNC sayHello();
+                int DLL_FUNC sum(int a, int b);
+
+                #ifdef __cplusplus
+                }
+                #endif
+        """)
+    }
+
+
+    List<SourceFile> librarySources = [
+        sourceFile("cpp", "hello.cpp", """
+            #include <iostream>
+            #include "hello.h"
+
+            void DLL_FUNC sayHello() {
+                #ifdef FRENCH
+                std::cout << "${HELLO_WORLD_FRENCH}" << std::endl;
+                #else
+                std::cout << "${HELLO_WORLD}" << std::endl;
+                #endif
+            }
+
+            int DLL_FUNC sum(int a, int b) {
+                return a + b;
+            }
+""")
+    ]
+}
diff --git a/subprojects/cpp/src/integTest/groovy/org/gradle/nativecode/language/cpp/fixtures/app/CHelloWorldApp.groovy b/subprojects/cpp/src/integTest/groovy/org/gradle/nativecode/language/cpp/fixtures/app/CHelloWorldApp.groovy
new file mode 100644
index 0000000..3f898c3
--- /dev/null
+++ b/subprojects/cpp/src/integTest/groovy/org/gradle/nativecode/language/cpp/fixtures/app/CHelloWorldApp.groovy
@@ -0,0 +1,112 @@
+/*
+ * Copyright 2013 the original author or authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.gradle.nativecode.language.cpp.fixtures.app
+
+class CHelloWorldApp extends IncrementalHelloWorldApp {
+
+    @Override
+    SourceFile getMainSource() {
+        sourceFile("c", "main.c", """
+            // Simple hello world app
+            #include <stdio.h>
+            #include "hello.h"
+
+            int main () {
+                sayHello();
+                printf("%d", sum(5, 7));
+                return 0;
+            }
+        """);
+    }
+
+    @Override
+    SourceFile getLibraryHeader() {
+        sourceFile("headers", "hello.h", """
+            #ifdef _WIN32
+            #define DLL_FUNC __declspec(dllexport)
+            #else
+            #define DLL_FUNC
+            #endif
+
+            void DLL_FUNC sayHello();
+            int DLL_FUNC sum(int a, int b);
+        """);
+    }
+
+    List<SourceFile> librarySources = [
+        sourceFile("c", "hello.c", """
+            #include <stdio.h>
+            #include "hello.h"
+
+            #ifdef FRENCH
+            char* greeting() {
+                return "${HELLO_WORLD_FRENCH}";
+            }
+            #endif
+            void DLL_FUNC sayHello() {
+                #ifdef FRENCH
+                printf("%s\\n", greeting());
+                #else
+                printf("${HELLO_WORLD}\\n");
+                #endif
+                fflush(stdout);
+            }
+        """),
+        sourceFile("c", "sum.c","""
+            #include "hello.h"
+
+            int DLL_FUNC sum(int a, int b) {
+                return a + b;
+            }
+        """)
+    ]
+
+    SourceFile getAlternateMainSource() {
+        sourceFile("c", "main.c", """
+            #include "hello.h"
+
+            int main () {
+              sayHello();
+              printf("goodbye");
+              return 0;
+            }
+        """)
+    }
+
+    String alternateOutput = "$HELLO_WORLD\ngoodbye"
+
+    List<SourceFile> alternateLibrarySources = [
+            sourceFile("c", "hello.c", """
+                #include <stdio.h>
+                #include "hello.h"
+
+                void DLL_FUNC sayHello() {
+                    printf("[${HELLO_WORLD} - ${HELLO_WORLD_FRENCH}]\\n");
+                    fflush(stdout);
+                }
+            """),
+            sourceFile("c", "sum.c","""
+                #include "hello.h"
+
+                int DLL_FUNC sum(int a, int b) {
+                    return a + b;
+                }
+            """)
+    ]
+
+    String alternateLibraryOutput = "[${HELLO_WORLD} - ${HELLO_WORLD_FRENCH}]\n12"
+}
diff --git a/subprojects/cpp/src/integTest/groovy/org/gradle/nativecode/language/cpp/fixtures/app/CppCallingCHelloWorldApp.groovy b/subprojects/cpp/src/integTest/groovy/org/gradle/nativecode/language/cpp/fixtures/app/CppCallingCHelloWorldApp.groovy
new file mode 100644
index 0000000..620cc00
--- /dev/null
+++ b/subprojects/cpp/src/integTest/groovy/org/gradle/nativecode/language/cpp/fixtures/app/CppCallingCHelloWorldApp.groovy
@@ -0,0 +1,70 @@
+/*
+ * Copyright 2013 the original author or authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.gradle.nativecode.language.cpp.fixtures.app;
+
+public class CppCallingCHelloWorldApp extends HelloWorldApp {
+    @Override
+    SourceFile getMainSource() {
+        sourceFile("cpp", "main.cpp", """
+            #include <iostream>
+            extern "C" {
+                #include "hello.h"
+            }
+
+            int main () {
+              sayHello();
+              std::cout << sum(5, 7);
+              return 0;
+            }
+        """)
+    }
+
+    @Override
+    SourceFile getLibraryHeader() {
+        sourceFile("headers", "hello.h", """
+            #ifdef _WIN32
+            #define DLL_FUNC __declspec(dllexport)
+            #else
+            #define DLL_FUNC
+            #endif
+
+            void DLL_FUNC sayHello();
+            int DLL_FUNC sum(int a, int b);
+        """);
+    }
+
+    List<SourceFile> librarySources = [
+        sourceFile("c", "hello.c", """
+            #include <stdio.h>
+            #include "hello.h"
+
+            void DLL_FUNC sayHello() {
+                #ifdef FRENCH
+                printf("${HELLO_WORLD_FRENCH}\\n");
+                #else
+                printf("${HELLO_WORLD}\\n");
+                #endif
+                fflush(stdout);
+            }
+
+            int DLL_FUNC sum(int a, int b) {
+                return a + b;
+            }
+        """)
+    ]
+
+}
diff --git a/subprojects/cpp/src/integTest/groovy/org/gradle/nativecode/language/cpp/fixtures/app/CppHelloWorldApp.groovy b/subprojects/cpp/src/integTest/groovy/org/gradle/nativecode/language/cpp/fixtures/app/CppHelloWorldApp.groovy
new file mode 100644
index 0000000..3cb212e
--- /dev/null
+++ b/subprojects/cpp/src/integTest/groovy/org/gradle/nativecode/language/cpp/fixtures/app/CppHelloWorldApp.groovy
@@ -0,0 +1,111 @@
+/*
+ * Copyright 2013 the original author or authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.gradle.nativecode.language.cpp.fixtures.app
+
+class CppHelloWorldApp extends IncrementalHelloWorldApp {
+    @Override
+    SourceFile getMainSource() {
+        return sourceFile("cpp", "main.cpp", """
+            // Simple hello world app
+            #include <iostream>
+            #include "hello.h"
+
+            int main () {
+              sayHello();
+              std::cout << sum(5, 7);
+              return 0;
+            }
+        """);
+    }
+
+    SourceFile getAlternateMainSource() {
+        sourceFile("cpp", "main.cpp", """
+            #include <iostream>
+            #include "hello.h"
+
+            int main () {
+              sayHello();
+              return 0;
+            }
+        """)
+    }
+
+    String alternateOutput = "$HELLO_WORLD\n"
+
+    @Override
+    SourceFile getLibraryHeader() {
+        return sourceFile("headers", "hello.h", """
+            #ifdef _WIN32
+            #define DLL_FUNC __declspec(dllexport)
+            #else
+            #define DLL_FUNC
+            #endif
+
+            void DLL_FUNC sayHello();
+            int DLL_FUNC sum(int a, int b);
+        """);
+    }
+
+    List<SourceFile> librarySources = [
+        sourceFile("cpp", "hello.cpp", """
+            #include <iostream>
+            #include "hello.h"
+
+            #ifdef FRENCH
+            const char* greeting() {
+                return "${HELLO_WORLD_FRENCH}";
+            }
+            #endif
+
+            void DLL_FUNC sayHello() {
+                #ifdef FRENCH
+                std::cout << greeting() << std::endl;
+                #else
+                std::cout << "${HELLO_WORLD}" << std::endl;
+                #endif
+            }
+        """),
+        sourceFile("cpp", "sum.cpp", """
+            #include "hello.h"
+
+            int DLL_FUNC sum(int a, int b) {
+                return a + b;
+            }
+        """)
+    ]
+
+    List<SourceFile> alternateLibrarySources = [
+        sourceFile("cpp", "hello.cpp", """
+            #include <iostream>
+            #include "hello.h"
+
+            void DLL_FUNC sayHello() {
+                std::cout << "[${HELLO_WORLD} - ${HELLO_WORLD_FRENCH}]" << std::endl;
+            }
+        """),
+        sourceFile("cpp", "sum.cpp", """
+            #include "hello.h"
+
+            int DLL_FUNC sum(int a, int b) {
+                return a + b;
+            }
+        """)
+    ]
+
+    String alternateLibraryOutput = "[${HELLO_WORLD} - ${HELLO_WORLD_FRENCH}]\n12"
+
+}
diff --git a/subprojects/cpp/src/integTest/groovy/org/gradle/nativecode/language/cpp/fixtures/app/HelloWorldApp.java b/subprojects/cpp/src/integTest/groovy/org/gradle/nativecode/language/cpp/fixtures/app/HelloWorldApp.java
new file mode 100644
index 0000000..e089767
--- /dev/null
+++ b/subprojects/cpp/src/integTest/groovy/org/gradle/nativecode/language/cpp/fixtures/app/HelloWorldApp.java
@@ -0,0 +1,73 @@
+/*
+ * Copyright 2013 the original author or authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.gradle.nativecode.language.cpp.fixtures.app;
+
+import org.gradle.test.fixtures.file.TestFile;
+import org.gradle.util.GUtil;
+
+import java.util.ArrayList;
+import java.util.List;
+
+public abstract class HelloWorldApp {
+    public static final String HELLO_WORLD = "Hello, World!";
+    public static final String HELLO_WORLD_FRENCH = "Bonjour, Monde!";
+
+    public String getEnglishOutput() {
+        return HELLO_WORLD + "\n12";
+    }
+
+    public String getFrenchOutput() {
+        return HELLO_WORLD_FRENCH + "\n12";
+    }
+
+    public String getCustomArgs() {
+        return "";
+    }
+
+    public String getSourceType() {
+        return GUtil.toCamelCase(getMainSource().getPath());
+    }
+
+    public abstract SourceFile getMainSource();
+    public abstract SourceFile getLibraryHeader();
+    public abstract List<SourceFile> getLibrarySources();
+
+    public List<SourceFile> getSourceFiles() {
+        ArrayList<SourceFile> sourceFiles = new ArrayList<SourceFile>();
+        sourceFiles.add(getMainSource());
+        sourceFiles.add(getLibraryHeader());
+        sourceFiles.addAll(getLibrarySources());
+        return sourceFiles;
+    }
+
+    protected SourceFile sourceFile(String path, String name, String content) {
+        return new SourceFile(path, name, content);
+    }
+
+    public void writeSources(TestFile mainSourceDir) {
+        writeSources(mainSourceDir, mainSourceDir);
+    }
+
+    public void writeSources(TestFile mainSourceDir, TestFile librarySourceDir) {
+        getMainSource().writeToDir(mainSourceDir);
+        getLibraryHeader().writeToDir(librarySourceDir);
+        for (SourceFile sourceFile : getLibrarySources()) {
+            sourceFile.writeToDir(librarySourceDir);
+        }
+    }
+
+}
diff --git a/subprojects/cpp/src/integTest/groovy/org/gradle/nativecode/language/cpp/fixtures/app/IncrementalHelloWorldApp.java b/subprojects/cpp/src/integTest/groovy/org/gradle/nativecode/language/cpp/fixtures/app/IncrementalHelloWorldApp.java
new file mode 100644
index 0000000..f8f26d8
--- /dev/null
+++ b/subprojects/cpp/src/integTest/groovy/org/gradle/nativecode/language/cpp/fixtures/app/IncrementalHelloWorldApp.java
@@ -0,0 +1,27 @@
+/*
+ * Copyright 2013 the original author or authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.gradle.nativecode.language.cpp.fixtures.app;
+
+import java.util.List;
+
+public abstract class IncrementalHelloWorldApp extends HelloWorldApp {
+    public abstract SourceFile getAlternateMainSource();
+    public abstract String getAlternateOutput();
+
+    public abstract List<SourceFile> getAlternateLibrarySources();
+    public abstract String getAlternateLibraryOutput();
+}
diff --git a/subprojects/cpp/src/integTest/groovy/org/gradle/nativecode/language/cpp/fixtures/app/MixedLanguageHelloWorldApp.groovy b/subprojects/cpp/src/integTest/groovy/org/gradle/nativecode/language/cpp/fixtures/app/MixedLanguageHelloWorldApp.groovy
new file mode 100644
index 0000000..92c0e33
--- /dev/null
+++ b/subprojects/cpp/src/integTest/groovy/org/gradle/nativecode/language/cpp/fixtures/app/MixedLanguageHelloWorldApp.groovy
@@ -0,0 +1,162 @@
+/*
+ * Copyright 2013 the original author or authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.gradle.nativecode.language.cpp.fixtures.app;
+
+import org.gradle.internal.os.OperatingSystem
+import org.gradle.nativecode.language.cpp.fixtures.AvailableToolChains;
+
+public class MixedLanguageHelloWorldApp extends HelloWorldApp {
+    private final AvailableToolChains.InstalledToolChain toolChain
+
+    MixedLanguageHelloWorldApp(AvailableToolChains.InstalledToolChain toolChain) {
+        this.toolChain = toolChain
+    }
+
+    String getCustomArgs() {
+        if (!toolChain.isVisualCpp()) {
+            // 32bit assembly on osx
+            if (OperatingSystem.current().isMacOsX()) {
+                return """
+                        compilerArgs "-m32"
+                        assemblerArgs "-arch", "i386"
+                        linkerArgs "-no_pie", "-arch", "i386"
+                """
+            }
+            // 32bit gcc assembly on windows
+            if (OperatingSystem.current().isWindows()) {
+                return """
+                        compilerArgs "-m32"
+                        assemblerArgs "-march", "i386"
+                """
+            }
+        }
+        return super.getCustomArgs()
+    }
+
+    SourceFile getMainSource() {
+        return new SourceFile("cpp", "main.cpp", """
+            #include <iostream>
+            extern "C" {
+                #include "hello.h"
+            }
+
+            int main () {
+              sayHello();
+              std::cout << sum(5, 7);
+              return 0;
+            }
+""")
+    }
+
+    SourceFile getLibraryHeader() {
+        return new SourceFile("headers", "hello.h", """
+            #ifdef _WIN32
+            #define DLL_FUNC __declspec(dllexport)
+            #else
+            #define DLL_FUNC
+            #endif
+
+            void DLL_FUNC sayHello();
+            int DLL_FUNC sum(int a, int b);
+""")
+    }
+
+    List<SourceFile> getLibrarySources() {
+        return  [
+            new SourceFile("c", "hello.c", """
+            #include <stdio.h>
+            #include "hello.h"
+
+            void DLL_FUNC sayHello() {
+                #ifdef FRENCH
+                printf("${HELLO_WORLD_FRENCH}\\n");
+                #else
+                printf("${HELLO_WORLD}\\n");
+                #endif
+
+                fflush(stdout);
+            }
+
+            int DLL_FUNC sum(int a, int b) {
+                return sumx(a, b);
+            }
+"""),
+            new SourceFile("asm", "sum.s", getAsmSource())
+        ]
+    }
+
+    protected def getAsmSource() {
+        def os = OperatingSystem.current()
+        if (os.isMacOsX()) {
+            return osxAsmSource
+        } else if (os.isWindows()) {
+            if (toolChain.isVisualCpp()) {
+                return windowsMasmSource
+            } else {
+                return i386GnuAsmSource;
+            }
+        } else {
+            return x64GnuAsmSource
+        }
+    }
+
+    private static String osxAsmSource = '''
+.section    __TEXT,__text,regular,pure_instructions
+.globl  _sumx
+.align  4
+_sumx:
+movl    8(%esp), %eax
+addl    4(%esp), %eax
+ret
+
+.subsections_via_symbols
+'''
+
+    private static String windowsMasmSource = '''
+.386
+.model    flat
+
+PUBLIC    _sumx
+_TEXT     SEGMENT
+_sumx    PROC
+mov    eax, DWORD PTR 4[esp]
+add    eax, DWORD PTR 8[esp]
+ret    0
+_sumx    ENDP
+_TEXT   ENDS
+END
+'''
+
+    private static String i386GnuAsmSource = '''
+    .text
+    .globl  _sumx
+_sumx:
+    movl    8(%esp), %eax
+    addl    4(%esp), %eax
+    ret
+'''
+
+    private static String x64GnuAsmSource = '''
+    .text
+    .p2align 4,,15
+.globl sumx
+    .type   sumx, @function
+sumx:
+    leal    (%rsi,%rdi), %eax
+    ret
+'''
+}
diff --git a/subprojects/cpp/src/integTest/groovy/org/gradle/nativecode/language/cpp/fixtures/app/SourceFile.java b/subprojects/cpp/src/integTest/groovy/org/gradle/nativecode/language/cpp/fixtures/app/SourceFile.java
new file mode 100644
index 0000000..3046c11
--- /dev/null
+++ b/subprojects/cpp/src/integTest/groovy/org/gradle/nativecode/language/cpp/fixtures/app/SourceFile.java
@@ -0,0 +1,52 @@
+/*
+ * Copyright 2013 the original author or authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.gradle.nativecode.language.cpp.fixtures.app;
+
+import org.gradle.test.fixtures.file.TestFile;
+
+public class SourceFile {
+    private final String path;
+    private final String name;
+    private final String content;
+
+    public SourceFile(String path, String name, String content) {
+        this.content = content;
+        this.path = path;
+        this.name = name;
+    }
+
+    public String getPath() {
+        return path;
+    }
+
+    public String getName() {
+        return name;
+    }
+
+    public String getContent() {
+        return content;
+    }
+
+    public TestFile writeToDir(TestFile base) {
+        TestFile file = base.file(path, name);
+        if (file.exists()) {
+            file.write("");
+        }
+        file.write(content);
+        return file;
+    }
+}
diff --git a/subprojects/cpp/src/integTest/groovy/org/gradle/plugins/cpp/AbstractBinariesIntegrationSpec.groovy b/subprojects/cpp/src/integTest/groovy/org/gradle/plugins/cpp/AbstractBinariesIntegrationSpec.groovy
deleted file mode 100755
index 4df0c8d..0000000
--- a/subprojects/cpp/src/integTest/groovy/org/gradle/plugins/cpp/AbstractBinariesIntegrationSpec.groovy
+++ /dev/null
@@ -1,33 +0,0 @@
-/*
- * Copyright 2012 the original author or authors.
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- *      http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS 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.cpp
-
-import org.gradle.integtests.fixtures.AbstractIntegrationSpec
-import org.gradle.internal.os.OperatingSystem
-import org.gradle.test.fixtures.file.TestFile
-import org.junit.runner.RunWith
-
- at RunWith(CppIntegrationTestRunner)
-abstract class AbstractBinariesIntegrationSpec extends AbstractIntegrationSpec {
-    def TestFile executable(Object path) {
-        return file(OperatingSystem.current().getExecutableName(path.toString()))
-    }
-
-    def TestFile sharedLibrary(Object path) {
-        return file(OperatingSystem.current().getSharedLibraryName(path.toString()))
-    }
-}
diff --git a/subprojects/cpp/src/integTest/groovy/org/gradle/plugins/cpp/AvailableCompilers.java b/subprojects/cpp/src/integTest/groovy/org/gradle/plugins/cpp/AvailableCompilers.java
deleted file mode 100755
index bfe89e7..0000000
--- a/subprojects/cpp/src/integTest/groovy/org/gradle/plugins/cpp/AvailableCompilers.java
+++ /dev/null
@@ -1,171 +0,0 @@
-/*
- * Copyright 2012 the original author or authors.
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- *      http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS 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.cpp;
-
-import org.gradle.internal.os.OperatingSystem;
-import org.gradle.plugins.cpp.gpp.internal.version.GppVersionDeterminer;
-
-import java.io.File;
-import java.util.*;
-
-public class AvailableCompilers {
-    static List<CompilerCandidate> getCompilers() {
-        List<CompilerCandidate> compilers = new ArrayList<CompilerCandidate>();
-        if (OperatingSystem.current().isWindows()) {
-            compilers.add(findVisualCpp());
-            compilers.add(findMinGW());
-        } else {
-            compilers.add(findGpp("3", "/opt/gcc/3.4.6/g++"));
-            compilers.add(findGpp("4", null));
-        }
-        return compilers;
-    }
-
-    static private CompilerCandidate findVisualCpp() {
-        // Search first in path, then in the standard installation locations
-        File compilerExe = OperatingSystem.current().findInPath("cl.exe");
-        if (compilerExe != null) {
-            return new InstalledCompiler("visual c++");
-        }
-
-        compilerExe = new File("C:/Program Files (x86)/Microsoft Visual Studio 10.0/VC/bin/cl.exe");
-        if (compilerExe.isFile()) {
-            File binDir = compilerExe.getParentFile();
-            File vcDir = binDir.getParentFile();
-            File baseDir = vcDir.getParentFile();
-            File sdkDir = new File(baseDir.getParentFile(), "Microsoft SDKs/Windows/v7.0A");
-            return new InstalledCompiler("visual c++",
-                    new File(baseDir, "Common7/IDE"), 
-                    binDir, 
-                    new File(baseDir, "Common7/Tools"), 
-                    new File(vcDir, "VCPackages"),
-                    new File(sdkDir, "Bin"))
-                    .envVar("INCLUDE", new File(vcDir, "include").getAbsolutePath())
-                    .envVar("LIB", new File(vcDir, "lib").getAbsolutePath() + File.pathSeparator + new File(sdkDir, "lib").getAbsolutePath());
-        }
-        
-        return new UnavailableCompiler("visual c++");
-    }
-
-    static private CompilerCandidate findMinGW() {
-        // Search in the standard installation locations (doesn't yet work with cygwin g++ in path)
-        File compilerExe = new File("C:/MinGW/bin/g++.exe");
-        if (compilerExe.isFile()) {
-            return new InstalledCompiler("mingw", compilerExe.getParentFile());
-        }
-
-        return new UnavailableCompiler("mingw");
-    }
-
-    static private CompilerCandidate findGpp(String versionPrefix, String hardcodedFallback) {
-        String name = String.format("g++ (%s)", versionPrefix);
-        GppVersionDeterminer versionDeterminer = new GppVersionDeterminer();
-        for (File candidate : OperatingSystem.current().findAllInPath("g++")) {
-            if (versionDeterminer.transform(candidate).startsWith(versionPrefix)) {
-                return new InstalledCompiler(name, candidate.getParentFile());
-            }
-        }
-
-        if (hardcodedFallback != null) {
-            File fallback = new File(hardcodedFallback);
-            if (fallback.isFile()) {
-                return new InstalledCompiler(name, fallback.getParentFile());
-            }
-        }
-
-        return new UnavailableCompiler(name);
-    }
-
-    public static abstract class CompilerCandidate {
-        @Override
-        public String toString() {
-            return getDisplayName();
-        }
-
-        public abstract String getDisplayName();
-        
-        public abstract boolean isAvailable();
-
-        public abstract List<File> getPathEntries();
-
-        public abstract Map<String, String> getEnvironmentVars();
-    }
-    
-    public static class InstalledCompiler extends CompilerCandidate {
-        private final List<File> pathEntries;
-        private final Map<String, String> environmentVars = new HashMap<String, String>();
-        private final String name;
-
-        public InstalledCompiler(String name, File... pathEntries) {
-            this.name = name;
-            this.pathEntries = Arrays.asList(pathEntries);
-        }
-
-        InstalledCompiler envVar(String key, String value) {
-            environmentVars.put(key, value);
-            return this;
-        }
-
-        @Override
-        public String getDisplayName() {
-            return name;
-        }
-
-        @Override
-        public boolean isAvailable() {
-            return true;
-        }
-
-        @Override
-        public List<File> getPathEntries() {
-            return pathEntries;
-        }
-
-        @Override
-        public Map<String, String> getEnvironmentVars() {
-            return environmentVars;
-        }
-    }
-
-    public static class UnavailableCompiler extends CompilerCandidate {
-        private final String name;
-
-        public UnavailableCompiler(String name) {
-            this.name = name;
-        }
-
-        @Override
-        public String getDisplayName() {
-            return name;
-        }
-
-        @Override
-        public boolean isAvailable() {
-            return false;
-        }
-
-        @Override
-        public List<File> getPathEntries() {
-            throw new UnsupportedOperationException("This compiler is not installed.");
-        }
-
-        @Override
-        public Map<String, String> getEnvironmentVars() {
-            throw new UnsupportedOperationException("This compiler is not installed.");
-        }
-    }
-}
diff --git a/subprojects/cpp/src/integTest/groovy/org/gradle/plugins/cpp/CppExePluginGoodBehaviourTest.groovy b/subprojects/cpp/src/integTest/groovy/org/gradle/plugins/cpp/CppExePluginGoodBehaviourTest.groovy
deleted file mode 100644
index 1401efb..0000000
--- a/subprojects/cpp/src/integTest/groovy/org/gradle/plugins/cpp/CppExePluginGoodBehaviourTest.groovy
+++ /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.plugins.cpp
-
-import org.gradle.integtests.fixtures.WellBehavedPluginTest
-
-class CppExePluginGoodBehaviourTest extends WellBehavedPluginTest {
-    @Override
-    def String getPluginId() {
-        return "cpp-exe"
-    }
-}
diff --git a/subprojects/cpp/src/integTest/groovy/org/gradle/plugins/cpp/CppIntegrationTestRunner.java b/subprojects/cpp/src/integTest/groovy/org/gradle/plugins/cpp/CppIntegrationTestRunner.java
deleted file mode 100755
index ee6733d..0000000
--- a/subprojects/cpp/src/integTest/groovy/org/gradle/plugins/cpp/CppIntegrationTestRunner.java
+++ /dev/null
@@ -1,96 +0,0 @@
-/*
- * Copyright 2012 the original author or authors.
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- *      http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS 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.cpp;
-
-import com.google.common.base.Joiner;
-import org.gradle.integtests.fixtures.AbstractMultiTestRunner;
-import org.gradle.internal.nativeplatform.*;
-import org.gradle.internal.nativeplatform.services.NativeServices;
-import org.gradle.internal.os.OperatingSystem;
-
-import java.io.File;
-import java.util.List;
-import java.util.Map;
-
-public class CppIntegrationTestRunner extends AbstractMultiTestRunner {
-    public CppIntegrationTestRunner(Class<?> target) {
-        super(target);
-    }
-
-    @Override
-    protected void createExecutions() {
-        List<AvailableCompilers.CompilerCandidate> compilers = AvailableCompilers.getCompilers();
-        for (AvailableCompilers.CompilerCandidate compiler : compilers) {
-            add(new CompilerExecution(compiler));
-        }
-    }
-
-    private static class CompilerExecution extends Execution {
-        private static final ProcessEnvironment PROCESS_ENVIRONMENT = NativeServices.getInstance().get(ProcessEnvironment.class);
-        private final AvailableCompilers.CompilerCandidate compiler;
-        private String originalPath;
-        private final String pathVarName;
-
-
-        public CompilerExecution(AvailableCompilers.CompilerCandidate compiler) {
-            this.compiler = compiler;
-            this.pathVarName = !OperatingSystem.current().isWindows() ? "Path" : "PATH";
-        }
-
-        @Override
-        protected boolean isEnabled() {
-            return compiler.isAvailable() && canDoNecessaryEnvironmentManipulation();
-        }
-
-        private boolean canDoNecessaryEnvironmentManipulation() {
-            return (compiler.getEnvironmentVars().isEmpty() && compiler.getPathEntries().isEmpty())
-                    || PROCESS_ENVIRONMENT.maybeSetEnvironmentVariable(pathVarName, System.getenv(pathVarName));
-        }
-
-        @Override
-        protected String getDisplayName() {
-            return compiler.getDisplayName();
-        }
-
-        @Override
-        protected void before() {
-            System.out.println(String.format("Using compiler %s", compiler.getDisplayName()));
-
-            String compilerPath = Joiner.on(File.pathSeparator).join(compiler.getPathEntries());
-
-
-            if (compilerPath.length() > 0) {
-                originalPath = System.getenv(pathVarName);
-                String path = compilerPath + File.pathSeparator + originalPath;
-                System.out.println(String.format("Using path %s", path));
-                PROCESS_ENVIRONMENT.setEnvironmentVariable(pathVarName, path);
-            }
-
-            for (Map.Entry<String, String> entry : compiler.getEnvironmentVars().entrySet()) {
-                System.out.println(String.format("Using environment var %s -> %s", entry.getKey(), entry.getValue()));
-                PROCESS_ENVIRONMENT.setEnvironmentVariable(entry.getKey(), entry.getValue());
-            }
-        }
-
-        @Override
-        protected void after() {
-            if (originalPath != null) {
-                PROCESS_ENVIRONMENT.setEnvironmentVariable(pathVarName, originalPath);
-            }
-        }
-    }
-}
diff --git a/subprojects/cpp/src/integTest/groovy/org/gradle/plugins/cpp/CppLibPluginGoodBehaviourTest.groovy b/subprojects/cpp/src/integTest/groovy/org/gradle/plugins/cpp/CppLibPluginGoodBehaviourTest.groovy
deleted file mode 100644
index 134b5d5..0000000
--- a/subprojects/cpp/src/integTest/groovy/org/gradle/plugins/cpp/CppLibPluginGoodBehaviourTest.groovy
+++ /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.plugins.cpp
-
-import org.gradle.integtests.fixtures.WellBehavedPluginTest
-
-class CppLibPluginGoodBehaviourTest extends WellBehavedPluginTest {
-    @Override
-    def String getPluginId() {
-        return "cpp-lib"
-    }
-}
diff --git a/subprojects/cpp/src/integTest/groovy/org/gradle/plugins/cpp/CppPluginIntegrationTest.groovy b/subprojects/cpp/src/integTest/groovy/org/gradle/plugins/cpp/CppPluginIntegrationTest.groovy
deleted file mode 100755
index a4a3f22..0000000
--- a/subprojects/cpp/src/integTest/groovy/org/gradle/plugins/cpp/CppPluginIntegrationTest.groovy
+++ /dev/null
@@ -1,220 +0,0 @@
-/*
- * Copyright 2012 the original author or authors.
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- *      http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS 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.cpp
-
-import static org.gradle.util.TextUtil.escapeString
-
-class CppPluginIntegrationTest extends AbstractBinariesIntegrationSpec {
-
-    static final HELLO_WORLD = "Hello, World!"
-
-    def "build and execute simple cpp program"() {
-        given:
-        buildFile << """
-            apply plugin: "cpp-exe"
-        """
-        settingsFile << "rootProject.name = 'test'"
-
-        and:
-        file("src", "main", "cpp", "helloworld.cpp") << """
-            #include <iostream>
-
-            int main () {
-              std::cout << "${escapeString(HELLO_WORLD)}";
-              return 0;
-            }
-        """
-
-        when:
-        run "compileMain"
-
-        then:
-        def executable = executable("build/binaries/test")
-        executable.isFile()
-        executable.exec().out == HELLO_WORLD
-    }
-
-    def "build simple cpp library"() {
-        given:
-        buildFile << """
-            apply plugin: "cpp-lib"
-        """
-        settingsFile << "rootProject.name = 'test'"
-
-        and:
-        file("src", "main", "cpp", "helloworld.cpp") << """
-            #include <iostream>
-            #ifdef _WIN32
-            #define DLL_FUNC __declspec(dllexport)
-            #else
-            #define DLL_FUNC
-            #endif
-
-            int DLL_FUNC main () {
-              std::cout << "${escapeString(HELLO_WORLD)}";
-              return 0;
-            }
-        """
-
-        when:
-        run "compileMain"
-
-        then:
-        sharedLibrary("build/binaries/test").isFile()
-    }
-
-    def "build fails when compilation fails"() {
-        given:
-        buildFile << """
-            apply plugin: "cpp-exe"
-        """
-        settingsFile << "rootProject.name = 'test'"
-
-        and:
-        file("src", "main", "cpp", "helloworld.cpp") << """
-            #include <iostream>
-
-            'broken
-        """
-
-        expect:
-        fails "compileMain"
-    }
-
-    def "build fails when link fails"() {
-        given:
-        buildFile << """
-            apply plugin: "cpp-exe"
-        """
-        settingsFile << "rootProject.name = 'test'"
-
-        and:
-        file("src", "main", "cpp", "helloworld.cpp") << """
-            int thing() { return 0; }
-        """
-
-        expect:
-        fails "compileMain"
-    }
-
-    def "build and execute program from multiple source files"() {
-        given:
-        buildFile << """
-            apply plugin: "cpp-exe"
-        """
-        settingsFile << "rootProject.name = 'test'"
-
-        and:
-        file("src", "main", "cpp", "hello.cpp") << """
-            #include <iostream>
-
-            void hello () {
-              std::cout << "${escapeString(HELLO_WORLD)}";
-            }
-        """
-
-        and:
-        file("src", "main", "headers", "hello.h") << """
-            void hello();
-        """
-
-        and:
-        file("src", "main", "cpp", "main.cpp") << """
-            #include "hello.h"
-
-            int main () {
-              hello();
-              return 0;
-            }
-        """
-
-        when:
-        run "compileMain"
-
-        then:
-        executable("build/binaries/test").exec().out == HELLO_WORLD
-    }
-
-    def "build, install and execute program with shared library"() {
-        given:
-        buildFile << """
-            apply plugin: "cpp-exe"
-
-            cpp {
-                sourceSets {
-                    hello {}
-                }
-            }
-            libraries {
-                hello {
-                    sourceSets << cpp.sourceSets.hello
-                }
-            }
-            cpp.sourceSets.main.libs << libraries.hello
-        """
-        settingsFile << "rootProject.name = 'test'"
-
-        and:
-        file("src/hello/cpp/hello.cpp") << """
-            #include <iostream>
-            #ifdef _WIN32
-            #define DLL_FUNC __declspec(dllexport)
-            #else
-            #define DLL_FUNC
-            #endif
-
-            void DLL_FUNC hello(const char* str) {
-              std::cout << str;
-            }
-        """
-
-        and:
-        file("src/hello/headers/hello.h") << """
-            void hello(const char* str);
-        """
-
-        and:
-        file("src/main/cpp/main.cpp") << """
-            #include <iostream>
-            #include "hello.h"
-
-            int main (int argc, char** argv) {
-              hello("${escapeString(HELLO_WORLD)}");
-              for ( int i = 1; i < argc; i++ ) {
-                std::cout << "[" << argv[i] << "]";
-              }
-              return 0;
-            }
-        """
-
-        when:
-        run "installMain"
-
-        then:
-        sharedLibrary("build/binaries/hello").isFile()
-        executable("build/binaries/test").isFile()
-
-        executable("build/install/main/test").exec().out == HELLO_WORLD
-        executable("build/install/main/test").exec("a", "1 2 3").out.contains("[a][1 2 3]")
-
-        // Ensure installed binary is not dependent on the libraries in their original locations
-        when:
-        file("build/binaries").deleteDir()
-
-        then:
-        executable("build/install/main/test").exec().out == HELLO_WORLD
-    }
-}
diff --git a/subprojects/cpp/src/integTest/groovy/org/gradle/plugins/cpp/CppSamplesIntegrationTest.groovy b/subprojects/cpp/src/integTest/groovy/org/gradle/plugins/cpp/CppSamplesIntegrationTest.groovy
deleted file mode 100755
index 5531fe7..0000000
--- a/subprojects/cpp/src/integTest/groovy/org/gradle/plugins/cpp/CppSamplesIntegrationTest.groovy
+++ /dev/null
@@ -1,85 +0,0 @@
-/*
- * Copyright 2012 the original author or authors.
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- *      http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS 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.cpp
-
-import org.gradle.integtests.fixtures.Sample
-import org.gradle.util.Requires
-import org.gradle.util.TestPrecondition
-import org.junit.Rule
-
-import static org.gradle.util.TextUtil.toPlatformLineSeparators
-
-class CppSamplesIntegrationTest extends AbstractBinariesIntegrationSpec {
-    @Rule public final Sample exewithlib = new Sample(temporaryFolder, 'cpp/exewithlib')
-    @Rule public final Sample dependencies = new Sample(temporaryFolder, 'cpp/dependencies')
-    @Rule public final Sample exe = new Sample(temporaryFolder, 'cpp/exe')
-
-    def "exe with lib"() {
-        given:
-        sample exewithlib
-
-        when:
-        run "installMain"
-
-        then:
-        ":exe:compileMain" in executedTasks
-
-        and:
-        sharedLibrary("cpp/exewithlib/lib/build/binaries/lib").isFile()
-        executable("cpp/exewithlib/exe/build/binaries/exe").isFile()
-        executable("cpp/exewithlib/exe/build/install/main/exe").exec().out == toPlatformLineSeparators("Hello, World!\n")
-    }
-
-    // Does not work on windows, due to GRADLE-2118
-    @Requires(TestPrecondition.NOT_WINDOWS)
-    def "dependencies"() {
-        when:
-        sample dependencies
-        run ":lib:uploadArchives"
-
-        then:
-        sharedLibrary("cpp/dependencies/lib/build/binaries/lib").isFile()
-        file("cpp/dependencies/lib/build/repo/some-org/some-lib/1.0/some-lib-1.0-so.so").isFile()
-
-        when:
-        sample dependencies
-        run ":exe:uploadArchives"
-        
-        then:
-        ":exe:mainExtractHeaders" in nonSkippedTasks
-        ":exe:compileMain" in nonSkippedTasks
-        
-        and:
-        executable("cpp/dependencies/exe/build/binaries/exe").isFile()
-        file("cpp/dependencies/exe/build/repo/dependencies/exe/1.0/exe-1.0.exe").exists()
-    }
-    
-    def "exe"() {
-        given:
-        sample exe
-        
-        when:
-        run "installMain"
-        
-        then:
-        ":compileMain" in nonSkippedTasks
-        
-        and:
-        executable("cpp/exe/build/binaries/exe").exec().out == toPlatformLineSeparators("Hello, World!\n")
-        executable("cpp/exe/build/install/main/exe").exec().out == toPlatformLineSeparators("Hello, World!\n")
-    }
-    
-}
\ No newline at end of file
diff --git a/subprojects/cpp/src/main/groovy/org/gradle/nativecode/base/DependentSourceSet.java b/subprojects/cpp/src/main/groovy/org/gradle/nativecode/base/DependentSourceSet.java
new file mode 100644
index 0000000..bc2b1e1
--- /dev/null
+++ b/subprojects/cpp/src/main/groovy/org/gradle/nativecode/base/DependentSourceSet.java
@@ -0,0 +1,45 @@
+/*
+ * Copyright 2013 the original author or authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.gradle.nativecode.base;
+
+import java.util.Collection;
+import java.util.Map;
+
+/**
+ * A source set that depends on one or more {@link NativeDependencySet}s to be built.
+ */
+public interface DependentSourceSet {
+    /**
+     * The libraries that this source set requires.
+     */
+    Collection<?> getLibs();
+
+    /**
+     * Adds a library that this source set requires. This method accepts the following types:
+     *
+     * <ul>
+     *     <li>A {@link Library}</li>
+     *     <li>A {@link LibraryBinary}</li>
+     *     <li>A {@link NativeDependencySet}</li>
+     * </ul>
+     */
+    void lib(Object library);
+
+    /**
+     * Add a dependency to this source set.
+     */
+    void dependency(Map<?, ?> dep);
+}
diff --git a/subprojects/cpp/src/main/groovy/org/gradle/nativecode/base/Executable.java b/subprojects/cpp/src/main/groovy/org/gradle/nativecode/base/Executable.java
new file mode 100644
index 0000000..5af455e
--- /dev/null
+++ b/subprojects/cpp/src/main/groovy/org/gradle/nativecode/base/Executable.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.nativecode.base;
+
+import org.gradle.api.Incubating;
+
+/**
+ * The logical representation of an executable native component.
+ */
+ at Incubating
+public interface Executable extends NativeComponent {
+    
+}
\ No newline at end of file
diff --git a/subprojects/cpp/src/main/groovy/org/gradle/nativecode/base/ExecutableBinary.java b/subprojects/cpp/src/main/groovy/org/gradle/nativecode/base/ExecutableBinary.java
new file mode 100644
index 0000000..6733bfb
--- /dev/null
+++ b/subprojects/cpp/src/main/groovy/org/gradle/nativecode/base/ExecutableBinary.java
@@ -0,0 +1,26 @@
+/*
+ * Copyright 2013 the original author or authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.gradle.nativecode.base;
+
+import org.gradle.api.Incubating;
+
+/**
+ * A binary artifact that is built from of a {@link Executable}, targeted at a particular platform with specific configuration.
+ */
+ at Incubating
+public interface ExecutableBinary extends NativeBinary {
+    Executable getComponent();
+}
\ No newline at end of file
diff --git a/subprojects/cpp/src/main/groovy/org/gradle/nativecode/base/ExecutableContainer.java b/subprojects/cpp/src/main/groovy/org/gradle/nativecode/base/ExecutableContainer.java
new file mode 100644
index 0000000..b4cd50e
--- /dev/null
+++ b/subprojects/cpp/src/main/groovy/org/gradle/nativecode/base/ExecutableContainer.java
@@ -0,0 +1,25 @@
+/*
+ * Copyright 2013 the original author or authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.gradle.nativecode.base;
+
+import org.gradle.api.NamedDomainObjectSet;
+
+/**
+ * A container of native executables.
+ */
+public interface ExecutableContainer extends NamedDomainObjectSet<Executable> {
+}
diff --git a/subprojects/cpp/src/main/groovy/org/gradle/nativecode/base/Flavor.java b/subprojects/cpp/src/main/groovy/org/gradle/nativecode/base/Flavor.java
new file mode 100644
index 0000000..16ae1f3
--- /dev/null
+++ b/subprojects/cpp/src/main/groovy/org/gradle/nativecode/base/Flavor.java
@@ -0,0 +1,34 @@
+/*
+ * Copyright 2013 the original author or authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.gradle.nativecode.base;
+
+import org.gradle.api.Incubating;
+import org.gradle.api.Named;
+import org.gradle.nativecode.base.internal.DefaultFlavor;
+
+/**
+ * Defines a custom variant that can be built for a {@link NativeComponent}.
+ */
+ at Incubating
+public interface Flavor extends Named {
+    Flavor DEFAULT = new DefaultFlavor("default", true);
+
+    /**
+     * Is this the automatically created default flavor for the component?
+     */
+    boolean isDefault();
+}
diff --git a/subprojects/cpp/src/main/groovy/org/gradle/nativecode/base/FlavorContainer.java b/subprojects/cpp/src/main/groovy/org/gradle/nativecode/base/FlavorContainer.java
new file mode 100644
index 0000000..3aea240
--- /dev/null
+++ b/subprojects/cpp/src/main/groovy/org/gradle/nativecode/base/FlavorContainer.java
@@ -0,0 +1,27 @@
+/*
+ * Copyright 2013 the original author or authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.gradle.nativecode.base;
+
+import org.gradle.api.NamedDomainObjectContainer;
+
+/**
+ * A container of flavors.
+ * If now flavor is explicitly configured, will contain a single {@link Flavor#DEFAULT}.
+ * Any flavors explicitly configured will overwrite the default flavor.
+ */
+public interface FlavorContainer extends NamedDomainObjectContainer<Flavor> {
+}
diff --git a/subprojects/cpp/src/main/groovy/org/gradle/nativecode/base/HeaderExportingSourceSet.java b/subprojects/cpp/src/main/groovy/org/gradle/nativecode/base/HeaderExportingSourceSet.java
new file mode 100644
index 0000000..66e686f
--- /dev/null
+++ b/subprojects/cpp/src/main/groovy/org/gradle/nativecode/base/HeaderExportingSourceSet.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.nativecode.base;
+
+import org.gradle.api.Action;
+import org.gradle.api.Incubating;
+import org.gradle.api.file.SourceDirectorySet;
+import org.gradle.language.base.LanguageSourceSet;
+
+/**
+ * A source set that exposes headers
+ */
+ at Incubating
+public interface HeaderExportingSourceSet extends LanguageSourceSet {
+
+    /**
+     * Configure the exported header directories.
+     */
+    void exportedHeaders(Action<SourceDirectorySet> config);
+
+    /**
+     * The headers as a directory set.
+     */
+    SourceDirectorySet getExportedHeaders();
+}
\ No newline at end of file
diff --git a/subprojects/cpp/src/main/groovy/org/gradle/nativecode/base/Library.java b/subprojects/cpp/src/main/groovy/org/gradle/nativecode/base/Library.java
new file mode 100644
index 0000000..6083d83
--- /dev/null
+++ b/subprojects/cpp/src/main/groovy/org/gradle/nativecode/base/Library.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.nativecode.base;
+
+import org.gradle.api.Incubating;
+import org.gradle.api.file.SourceDirectorySet;
+
+/**
+ * The logical representation of an library native component.
+ */
+ at Incubating
+public interface Library extends NativeComponent {
+    /**
+     * The headers exported by this library.
+     */
+    SourceDirectorySet getHeaders();
+
+    /**
+     * Converts this library to a native dependency that uses the shared library variant. This is the default.
+     */
+    LibraryResolver getShared();
+
+    /**
+     * Converts this library to a native dependency that uses the static library variant.
+     */
+    LibraryResolver getStatic();
+}
\ No newline at end of file
diff --git a/subprojects/cpp/src/main/groovy/org/gradle/nativecode/base/LibraryBinary.java b/subprojects/cpp/src/main/groovy/org/gradle/nativecode/base/LibraryBinary.java
new file mode 100644
index 0000000..5680ebe
--- /dev/null
+++ b/subprojects/cpp/src/main/groovy/org/gradle/nativecode/base/LibraryBinary.java
@@ -0,0 +1,30 @@
+/*
+ * Copyright 2013 the original author or authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.gradle.nativecode.base;
+
+import org.gradle.api.Incubating;
+
+/**
+ * A physical representation of a {@link Library} component.
+ */
+ at Incubating
+public interface LibraryBinary extends NativeBinary, LibraryResolver {
+    /**
+     * The Library that this binary represents.
+     */
+    Library getComponent();
+}
diff --git a/subprojects/cpp/src/main/groovy/org/gradle/nativecode/base/LibraryContainer.java b/subprojects/cpp/src/main/groovy/org/gradle/nativecode/base/LibraryContainer.java
new file mode 100644
index 0000000..47025a1
--- /dev/null
+++ b/subprojects/cpp/src/main/groovy/org/gradle/nativecode/base/LibraryContainer.java
@@ -0,0 +1,25 @@
+/*
+ * Copyright 2013 the original author or authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.gradle.nativecode.base;
+
+import org.gradle.api.NamedDomainObjectSet;
+
+/**
+ * A container of C++ libraries.
+ */
+public interface LibraryContainer extends NamedDomainObjectSet<Library> {
+}
diff --git a/subprojects/cpp/src/main/groovy/org/gradle/nativecode/base/LibraryResolver.java b/subprojects/cpp/src/main/groovy/org/gradle/nativecode/base/LibraryResolver.java
new file mode 100644
index 0000000..2bd1819
--- /dev/null
+++ b/subprojects/cpp/src/main/groovy/org/gradle/nativecode/base/LibraryResolver.java
@@ -0,0 +1,32 @@
+/*
+ * Copyright 2013 the original author or authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.gradle.nativecode.base;
+
+import org.gradle.api.Incubating;
+
+// TODO:DAZ Needs a better name
+/**
+ * Resolves a library to a single binary to use as a dependency.
+ */
+ at Incubating
+public interface LibraryResolver {
+
+    /**
+     * Resolves the library to a dependency set.
+     */
+    NativeDependencySet resolve();
+}
diff --git a/subprojects/cpp/src/main/groovy/org/gradle/nativecode/base/NativeBinary.java b/subprojects/cpp/src/main/groovy/org/gradle/nativecode/base/NativeBinary.java
new file mode 100644
index 0000000..cfb57c2
--- /dev/null
+++ b/subprojects/cpp/src/main/groovy/org/gradle/nativecode/base/NativeBinary.java
@@ -0,0 +1,131 @@
+/*
+ * Copyright 2013 the original author or authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.gradle.nativecode.base;
+
+import org.gradle.api.DomainObjectSet;
+import org.gradle.api.Incubating;
+import org.gradle.api.internal.HasInternalProtocol;
+import org.gradle.language.base.Binary;
+import org.gradle.language.base.LanguageSourceSet;
+
+import java.io.File;
+import java.util.Collection;
+import java.util.List;
+
+// TODO:DAZ These don't apply to all binary subtypes: look at splitting this up in to a number of smaller facets / functional interfaces
+/**
+ * Represents a particular binary artifact that is the result of building a native component.
+ */
+ at Incubating @HasInternalProtocol
+public interface NativeBinary extends Binary {
+
+    /**
+     * The flavor that this binary was built with.
+     */
+    Flavor getFlavor();
+
+    /**
+     * The file where this binary will be created.
+     */
+    File getOutputFile();
+
+    /**
+     * Sets the file where this binary will be created.
+     */
+    void setOutputFile(File outputFile);
+
+    /**
+     * The source sets used to compile this binary.
+     */
+    DomainObjectSet<LanguageSourceSet> getSource();
+
+    /**
+     * Adds one or more {@link LanguageSourceSet}s that are used to compile this binary.
+     * <p/>
+     * This method accepts the following types:
+     *
+     * <ul>
+     *     <li>A {@link org.gradle.language.base.FunctionalSourceSet}</li>
+     *     <li>A {@link LanguageSourceSet}</li>
+     *     <li>A Collection of {@link LanguageSourceSet}s</li>
+     * </ul>
+     */
+    void source(Object source);
+
+    /**
+     * Returns the {@link ToolChain} that will be used to build this binary.
+     */
+    ToolChain getToolChain();
+
+    /**
+     * The libraries that should be linked into this binary.
+     */
+    Collection<NativeDependencySet> getLibs();
+
+    /**
+     * Adds a library as input to this binary.
+     * <p/>
+     * This method accepts the following types:
+     *
+     * <ul>
+     *     <li>A {@link Library}</li>
+     *     <li>A {@link LibraryBinary}</li>
+     *     <li>A {@link NativeDependencySet}</li>
+     * </ul>
+     */
+    void lib(Object library);
+
+    /**
+     * The set of preprocessor macros to define when compiling this binary.
+     */
+    List<Object> getMacros();
+
+    /**
+     * Adds a number of preprocessor macros to define when compiling this binary.
+     */
+    void define(Object... defines);
+
+    /**
+     * The arguments passed when compiling this binary.
+     */
+    List<Object> getCompilerArgs();
+
+    /**
+     * Adds a number of arguments to be passed to the compiler.
+     */
+    void compilerArgs(Object... args);
+
+    /**
+     * The arguments passed when assembling this binary.
+     */
+    List<Object> getAssemblerArgs();
+
+    /**
+     * Adds a number of arguments to be passed to the assembler.
+     */
+    void assemblerArgs(Object... args);
+
+    /**
+     * The arguments passed when linking this binary.
+     */
+    List<Object> getLinkerArgs();
+
+    /**
+     * Adds a number of arguments to be passed to the linker.
+     */
+    void linkerArgs(Object... args);
+}
diff --git a/subprojects/cpp/src/main/groovy/org/gradle/nativecode/base/NativeComponent.java b/subprojects/cpp/src/main/groovy/org/gradle/nativecode/base/NativeComponent.java
new file mode 100644
index 0000000..4fa0507
--- /dev/null
+++ b/subprojects/cpp/src/main/groovy/org/gradle/nativecode/base/NativeComponent.java
@@ -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.nativecode.base;
+
+import org.gradle.api.Action;
+import org.gradle.api.DomainObjectSet;
+import org.gradle.api.Incubating;
+import org.gradle.api.Named;
+import org.gradle.language.base.LanguageSourceSet;
+
+/**
+ * Represents a logical software component, which may be built in a number of variant binaries.
+ */
+ at Incubating
+public interface NativeComponent extends Named {
+
+    /**
+     * The source sets that are used to build this component.
+     */
+    DomainObjectSet<LanguageSourceSet> getSource();
+
+    /**
+     * Adds one or more {@link LanguageSourceSet}s that are used to compile this binary.
+     * <p/>
+     * This method accepts the following types:
+     *
+     * <ul>
+     *     <li>A {@link org.gradle.language.base.FunctionalSourceSet}</li>
+     *     <li>A {@link LanguageSourceSet}</li>
+     *     <li>A Collection of {@link LanguageSourceSet}s</li>
+     * </ul>
+     */
+    void source(Object source);
+
+    /**
+     * The binaries that are built for this component. You can use this to configure the binaries for this component.
+     */
+    DomainObjectSet<NativeBinary> getBinaries();
+
+    /**
+     * The name that is used to construct the output file names when building this component.
+     */
+    String getBaseName();
+
+    /**
+     * Sets the name that is used to construct the output file names when building this component.
+     */
+    void setBaseName(String baseName);
+
+    /**
+     * The set of flavors defined for this component. All components automatically have a default flavor named "default".
+     */
+    FlavorContainer getFlavors();
+
+    /**
+     * Configure the flavors for this component.
+     */
+    void flavors(Action<FlavorContainer> config);
+
+}
diff --git a/subprojects/cpp/src/main/groovy/org/gradle/nativecode/base/NativeDependencySet.java b/subprojects/cpp/src/main/groovy/org/gradle/nativecode/base/NativeDependencySet.java
new file mode 100644
index 0000000..3395f1c
--- /dev/null
+++ b/subprojects/cpp/src/main/groovy/org/gradle/nativecode/base/NativeDependencySet.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.nativecode.base;
+
+import org.gradle.api.Incubating;
+import org.gradle.api.file.FileCollection;
+
+/**
+ * Models a collection of native type dependencies.
+ */
+ at Incubating
+public interface NativeDependencySet {
+    /**
+     * Returns the header file directories to use at compile time.
+     */
+    FileCollection getIncludeRoots();
+
+    /**
+     * Returns the files to use at link time.
+     */
+    FileCollection getLinkFiles();
+
+    /**
+     * Returns the files to use at runtime.
+     */
+    FileCollection getRuntimeFiles();
+}
\ No newline at end of file
diff --git a/subprojects/cpp/src/main/groovy/org/gradle/nativecode/base/SharedLibraryBinary.java b/subprojects/cpp/src/main/groovy/org/gradle/nativecode/base/SharedLibraryBinary.java
new file mode 100644
index 0000000..9844891
--- /dev/null
+++ b/subprojects/cpp/src/main/groovy/org/gradle/nativecode/base/SharedLibraryBinary.java
@@ -0,0 +1,25 @@
+/*
+ * Copyright 2013 the original author or authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.gradle.nativecode.base;
+
+import org.gradle.api.Incubating;
+
+/**
+ * A {@link Library} that has been compiled and linked as a shared library.
+ */
+ at Incubating
+public interface SharedLibraryBinary extends LibraryBinary {
+}
\ No newline at end of file
diff --git a/subprojects/cpp/src/main/groovy/org/gradle/nativecode/base/StaticLibraryBinary.java b/subprojects/cpp/src/main/groovy/org/gradle/nativecode/base/StaticLibraryBinary.java
new file mode 100644
index 0000000..1bb3b27
--- /dev/null
+++ b/subprojects/cpp/src/main/groovy/org/gradle/nativecode/base/StaticLibraryBinary.java
@@ -0,0 +1,37 @@
+/*
+ * Copyright 2013 the original author or authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.gradle.nativecode.base;
+
+import org.gradle.api.Incubating;
+
+import java.util.List;
+
+/**
+ * A {@link Library} that has been compiled and archived into a static library.
+ */
+ at Incubating
+public interface StaticLibraryBinary extends LibraryBinary {
+    /**
+     * The arguments passed when creating a this binary.
+     */
+    List<Object> getStaticLibArgs();
+
+    /**
+     * Adds a number of arguments to be passed when creating this static library.
+     */
+    void staticLibArgs(Object... args);
+}
\ No newline at end of file
diff --git a/subprojects/cpp/src/main/groovy/org/gradle/nativecode/base/ToolChain.java b/subprojects/cpp/src/main/groovy/org/gradle/nativecode/base/ToolChain.java
new file mode 100644
index 0000000..2732f22
--- /dev/null
+++ b/subprojects/cpp/src/main/groovy/org/gradle/nativecode/base/ToolChain.java
@@ -0,0 +1,28 @@
+/*
+ * Copyright 2013 the original author or authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.gradle.nativecode.base;
+
+import org.gradle.api.Incubating;
+import org.gradle.api.Named;
+import org.gradle.api.internal.HasInternalProtocol;
+
+/**
+ * A set of compilers and linkers that are used together to construct a native binary.
+ */
+ at Incubating @HasInternalProtocol
+public interface ToolChain extends Named {
+}
diff --git a/subprojects/cpp/src/main/groovy/org/gradle/nativecode/base/ToolChainRegistry.java b/subprojects/cpp/src/main/groovy/org/gradle/nativecode/base/ToolChainRegistry.java
new file mode 100644
index 0000000..461c15f
--- /dev/null
+++ b/subprojects/cpp/src/main/groovy/org/gradle/nativecode/base/ToolChainRegistry.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.nativecode.base;
+
+import org.gradle.api.ExtensiblePolymorphicDomainObjectContainer;
+import org.gradle.api.Incubating;
+
+import java.util.List;
+
+/**
+ * A container for {@link ToolChain}s.
+ */
+ at Incubating
+public interface ToolChainRegistry extends ExtensiblePolymorphicDomainObjectContainer<ToolChain> {
+    /**
+     * Registers a default ToolChain. If no tool chain currently exists, and the registered tool
+     * chain is available, then a default instance is added to the registry.
+     * Creating or adding a ToolChain directly will replace a default instance.
+     */
+    void registerDefaultToolChain(String name, Class<? extends ToolChain> type);
+
+    /**
+     * Returns all registered {@link ToolChain}s that are available.
+     */
+    List<? extends ToolChain> getAvailableToolChains();
+
+    /**
+     * Returns the first registered {@link ToolChain} that is available.
+     */
+    ToolChain getDefaultToolChain();
+}
\ No newline at end of file
diff --git a/subprojects/cpp/src/main/groovy/org/gradle/nativecode/base/internal/AbstractToolChain.java b/subprojects/cpp/src/main/groovy/org/gradle/nativecode/base/internal/AbstractToolChain.java
new file mode 100644
index 0000000..c6d2ad3
--- /dev/null
+++ b/subprojects/cpp/src/main/groovy/org/gradle/nativecode/base/internal/AbstractToolChain.java
@@ -0,0 +1,139 @@
+/*
+ * Copyright 2013 the original author or authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.gradle.nativecode.base.internal;
+
+import org.gradle.api.internal.file.FileResolver;
+import org.gradle.internal.os.OperatingSystem;
+import org.gradle.nativecode.toolchain.ConfigurableToolChain;
+import org.gradle.nativecode.toolchain.Tool;
+import org.gradle.nativecode.toolchain.internal.ToolRegistry;
+import org.gradle.nativecode.toolchain.internal.ToolType;
+
+import java.io.File;
+import java.util.List;
+
+public abstract class AbstractToolChain implements ToolChainInternal, ConfigurableToolChain {
+    private final String name;
+    protected final OperatingSystem operatingSystem;
+    protected final ToolRegistry tools;
+    private final FileResolver fileResolver;
+    private ToolChainAvailability availability;
+
+    protected AbstractToolChain(String name, OperatingSystem operatingSystem, ToolRegistry tools, FileResolver fileResolver) {
+        this.name = name;
+        this.operatingSystem = operatingSystem;
+        this.tools = tools;
+        this.fileResolver = fileResolver;
+    }
+
+    public String getName() {
+        return name;
+    }
+
+    protected abstract String getTypeName();
+
+    @Override
+    public String toString() {
+        return String.format("ToolChain '%s' (%s)", getName(), getTypeName());
+    }
+
+    public ToolChainAvailability getAvailability() {
+        if (availability == null) {
+            availability = new ToolChainAvailability();
+            checkAvailable(availability);
+        }
+        return availability;
+    }
+
+    protected void checkAvailable() {
+        if (!getAvailability().isAvailable()) {
+            throw new IllegalStateException(String.format("Tool chain %s is not available", getName()));
+        }
+    }
+
+    public String getOutputType() {
+        return String.format("%s-%s", getName(), operatingSystem.getName());
+    }
+
+    protected abstract void checkAvailable(ToolChainAvailability availability);
+
+    public String getExecutableName(String executablePath) {
+        return operatingSystem.getExecutableName(executablePath);
+    }
+
+    public String getSharedLibraryName(String libraryName) {
+        return operatingSystem.getSharedLibraryName(libraryName);
+    }
+
+    public String getSharedLibraryLinkFileName(String libraryName) {
+        return getSharedLibraryName(libraryName);
+    }
+
+    public String getStaticLibraryName(String libraryName) {
+        return operatingSystem.getStaticLibraryName(libraryName);
+    }
+
+    public List<File> getPaths() {
+        return tools.getPath();
+    }
+
+    public void path(Object... paths) {
+        for (Object path : paths) {
+            tools.path(resolve(path));
+        }
+    }
+
+    protected File resolve(Object path) {
+        return fileResolver.resolve(path);
+    }
+
+    public Tool getCppCompiler() {
+        return new DefaultTool(ToolType.CPP_COMPILER);
+    }
+
+    public Tool getCCompiler() {
+        return new DefaultTool(ToolType.C_COMPILER);
+    }
+
+    public Tool getAssembler() {
+        return new DefaultTool(ToolType.ASSEMBLER);
+    }
+
+    public Tool getLinker() {
+        return new DefaultTool(ToolType.LINKER);
+    }
+
+    public Tool getStaticLibArchiver() {
+        return new DefaultTool(ToolType.STATIC_LIB_ARCHIVER);
+    }
+
+    private class DefaultTool implements Tool {
+        private final ToolType toolType;
+
+        private DefaultTool(ToolType toolType) {
+            this.toolType = toolType;
+        }
+
+        public String getExe() {
+            return tools.getExeName(toolType);
+        }
+
+        public void setExe(String file) {
+            tools.setExeName(toolType, file);
+        }
+    }
+}
diff --git a/subprojects/cpp/src/main/groovy/org/gradle/nativecode/base/internal/BinaryToolSpec.java b/subprojects/cpp/src/main/groovy/org/gradle/nativecode/base/internal/BinaryToolSpec.java
new file mode 100644
index 0000000..b70fac5
--- /dev/null
+++ b/subprojects/cpp/src/main/groovy/org/gradle/nativecode/base/internal/BinaryToolSpec.java
@@ -0,0 +1,27 @@
+/*
+ * Copyright 2013 the original author or authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.gradle.nativecode.base.internal;
+
+import org.gradle.api.internal.tasks.compile.CompileSpec;
+
+import java.io.File;
+
+public interface BinaryToolSpec extends CompileSpec {
+    File getTempDir();
+
+    void setTempDir(File tempDir);
+}
diff --git a/subprojects/cpp/src/main/groovy/org/gradle/nativecode/base/internal/ConfigurableLibraryResolver.java b/subprojects/cpp/src/main/groovy/org/gradle/nativecode/base/internal/ConfigurableLibraryResolver.java
new file mode 100644
index 0000000..37235a7
--- /dev/null
+++ b/subprojects/cpp/src/main/groovy/org/gradle/nativecode/base/internal/ConfigurableLibraryResolver.java
@@ -0,0 +1,27 @@
+/*
+ * Copyright 2013 the original author or authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.gradle.nativecode.base.internal;
+
+import org.gradle.nativecode.base.Flavor;
+import org.gradle.nativecode.base.LibraryBinary;
+import org.gradle.nativecode.base.LibraryResolver;
+
+public interface ConfigurableLibraryResolver extends LibraryResolver {
+    ConfigurableLibraryResolver withType(Class<? extends LibraryBinary> type);
+
+    ConfigurableLibraryResolver withFlavor(Flavor flavor);
+}
diff --git a/subprojects/cpp/src/main/groovy/org/gradle/nativecode/base/internal/ConfigurationBasedNativeDependencySet.groovy b/subprojects/cpp/src/main/groovy/org/gradle/nativecode/base/internal/ConfigurationBasedNativeDependencySet.groovy
new file mode 100644
index 0000000..4d431c8
--- /dev/null
+++ b/subprojects/cpp/src/main/groovy/org/gradle/nativecode/base/internal/ConfigurationBasedNativeDependencySet.groovy
@@ -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.nativecode.base.internal
+
+import org.gradle.nativecode.base.NativeDependencySet
+import org.gradle.api.Project
+import org.gradle.api.Task
+import org.gradle.api.file.FileCollection
+import org.gradle.api.artifacts.Configuration
+
+class ConfigurationBasedNativeDependencySet implements NativeDependencySet {
+
+    private final String baseName
+    final String headersConfigurationName
+    final String filesConfigurationName // files is a bad name
+    final Project project
+    private Task headerExtractionTask
+
+    ConfigurationBasedNativeDependencySet(Project project, String baseName = "main") {
+        this.baseName = baseName
+        this.headersConfigurationName = baseName + "HeaderDependencies"
+        this.filesConfigurationName = baseName + "FileDependencies"
+        this.project = project
+
+        createConfigurations()
+        initHeaderExtractionTask()
+    }
+
+    private createConfigurations() {
+        project.configurations.with {
+            create(headersConfigurationName)
+            create(filesConfigurationName)
+        }
+    }
+
+    private initHeaderExtractionTask() {
+        def headersConfiguration = getHeadersConfiguration()
+        def dir = project.file("$project.buildDir/dependency-headers/$baseName")
+        headerExtractionTask = project.task(baseName + "ExtractHeaders") {
+            inputs.files headersConfiguration
+            outputs.files { dir.listFiles() }
+            doLast {
+                headersConfiguration.each { headerZip ->
+                    project.copy {
+                        from project.zipTree(headerZip)
+                        into "$dir/${headerZip.name - '.zip'}"
+                    }
+                }
+            }
+        }
+    }
+
+    Configuration getHeadersConfiguration() {
+        project.configurations[headersConfigurationName]
+    }
+
+    FileCollection getIncludeRoots() {
+        headerExtractionTask.outputs.files
+    }
+
+    FileCollection getLinkFiles() {
+        project.configurations[filesConfigurationName]
+    }
+
+    FileCollection getRuntimeFiles() {
+        return getLinkFiles()
+    }
+
+    void add(Map dep) {
+        // hackity hack hack
+        project.dependencies {
+            def m = { classifier, ext -> [classifier: classifier, ext: ext] }
+            delegate."$headersConfigurationName"(dep + m("headers", "zip"))
+            delegate."$filesConfigurationName"(dep + m("so", "so"))
+        }
+    }
+}
diff --git a/subprojects/cpp/src/main/groovy/org/gradle/nativecode/base/internal/CreateNativeBinariesAction.java b/subprojects/cpp/src/main/groovy/org/gradle/nativecode/base/internal/CreateNativeBinariesAction.java
new file mode 100644
index 0000000..0c6a7c4
--- /dev/null
+++ b/subprojects/cpp/src/main/groovy/org/gradle/nativecode/base/internal/CreateNativeBinariesAction.java
@@ -0,0 +1,57 @@
+/*
+ * Copyright 2013 the original author or authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.gradle.nativecode.base.internal;
+
+import org.gradle.api.Action;
+import org.gradle.api.internal.project.ProjectInternal;
+import org.gradle.internal.reflect.Instantiator;
+import org.gradle.language.base.BinaryContainer;
+import org.gradle.nativecode.base.*;
+
+import java.util.List;
+
+public class CreateNativeBinariesAction implements Action<ProjectInternal> {
+    private final Instantiator instantiator;
+
+    public CreateNativeBinariesAction(Instantiator instantiator) {
+        this.instantiator = instantiator;
+    }
+
+    public void execute(ProjectInternal project) {
+        ToolChainRegistry toolChains = project.getExtensions().getByType(ToolChainRegistry.class);
+        ExecutableContainer executables = project.getExtensions().getByType(ExecutableContainer.class);
+        LibraryContainer libraries = project.getExtensions().getByType(LibraryContainer.class);
+        BinaryContainer binaries = project.getExtensions().getByType(BinaryContainer.class);
+
+        NativeBinaryFactory factory = new NativeBinaryFactory(instantiator, project, toolChains);
+        List<? extends ToolChain> availableToolChains = toolChains.getAvailableToolChains();
+        for (ToolChain toolChain : availableToolChains) {
+            for (Library library : libraries) {
+                for (Flavor flavor : library.getFlavors()) {
+                    binaries.add(factory.createNativeBinary(DefaultSharedLibraryBinary.class, library, toolChain, flavor));
+                    binaries.add(factory.createNativeBinary(DefaultStaticLibraryBinary.class, library, toolChain, flavor));
+                }
+            }
+            for (Executable executable : executables) {
+                for (Flavor flavor : executable.getFlavors()) {
+                    binaries.add(factory.createNativeBinary(DefaultExecutableBinary.class, executable, toolChain, flavor));
+                }
+            }
+        }
+    }
+
+}
diff --git a/subprojects/cpp/src/main/groovy/org/gradle/nativecode/base/internal/DefaultExecutable.java b/subprojects/cpp/src/main/groovy/org/gradle/nativecode/base/internal/DefaultExecutable.java
new file mode 100755
index 0000000..b6223d0
--- /dev/null
+++ b/subprojects/cpp/src/main/groovy/org/gradle/nativecode/base/internal/DefaultExecutable.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.nativecode.base.internal;
+
+import org.gradle.internal.reflect.Instantiator;
+import org.gradle.nativecode.base.Executable;
+
+public class DefaultExecutable extends DefaultNativeComponent implements Executable {
+    public DefaultExecutable(String name, Instantiator instantiator) {
+        super(name, instantiator);
+    }
+
+    @Override
+    public String toString() {
+        return String.format("executable '%s'", getName());
+    }
+}
\ No newline at end of file
diff --git a/subprojects/cpp/src/main/groovy/org/gradle/nativecode/base/internal/DefaultExecutableBinary.java b/subprojects/cpp/src/main/groovy/org/gradle/nativecode/base/internal/DefaultExecutableBinary.java
new file mode 100644
index 0000000..dcb9144
--- /dev/null
+++ b/subprojects/cpp/src/main/groovy/org/gradle/nativecode/base/internal/DefaultExecutableBinary.java
@@ -0,0 +1,39 @@
+/*
+ * Copyright 2013 the original author or authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.gradle.nativecode.base.internal;
+
+import org.gradle.language.base.internal.DefaultBinaryNamingScheme;
+import org.gradle.nativecode.base.Executable;
+import org.gradle.nativecode.base.ExecutableBinary;
+import org.gradle.nativecode.base.Flavor;
+
+public class DefaultExecutableBinary extends DefaultNativeBinary implements ExecutableBinary {
+    private final Executable executable;
+
+    public DefaultExecutableBinary(Executable executable, Flavor flavor, ToolChainInternal toolChain, DefaultBinaryNamingScheme namingScheme) {
+        super(executable, flavor, toolChain, namingScheme.withTypeString("Executable"));
+        this.executable = executable;
+    }
+
+    public Executable getComponent() {
+        return executable;
+    }
+
+    public String getOutputFileName() {
+        return getToolChain().getExecutableName(getComponent().getBaseName());
+    }
+}
diff --git a/subprojects/cpp/src/main/groovy/org/gradle/nativecode/base/internal/DefaultExecutableContainer.java b/subprojects/cpp/src/main/groovy/org/gradle/nativecode/base/internal/DefaultExecutableContainer.java
new file mode 100644
index 0000000..62aa6b5
--- /dev/null
+++ b/subprojects/cpp/src/main/groovy/org/gradle/nativecode/base/internal/DefaultExecutableContainer.java
@@ -0,0 +1,33 @@
+/*
+ * Copyright 2013 the original author or authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.gradle.nativecode.base.internal;
+
+import org.gradle.api.internal.AbstractNamedDomainObjectContainer;
+import org.gradle.internal.reflect.Instantiator;
+import org.gradle.nativecode.base.Executable;
+import org.gradle.nativecode.base.ExecutableContainer;
+
+public class DefaultExecutableContainer extends AbstractNamedDomainObjectContainer<Executable> implements ExecutableContainer {
+    public DefaultExecutableContainer(Instantiator instantiator) {
+        super(Executable.class, instantiator);
+    }
+
+    @Override
+    protected Executable doCreate(String name) {
+        return getInstantiator().newInstance(DefaultExecutable.class, name, getInstantiator());
+    }
+}
diff --git a/subprojects/cpp/src/main/groovy/org/gradle/nativecode/base/internal/DefaultFlavor.java b/subprojects/cpp/src/main/groovy/org/gradle/nativecode/base/internal/DefaultFlavor.java
new file mode 100644
index 0000000..56063be
--- /dev/null
+++ b/subprojects/cpp/src/main/groovy/org/gradle/nativecode/base/internal/DefaultFlavor.java
@@ -0,0 +1,65 @@
+/*
+ * Copyright 2013 the original author or authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.gradle.nativecode.base.internal;
+
+import org.gradle.nativecode.base.Flavor;
+
+public class DefaultFlavor implements Flavor {
+    private final String name;
+    private final boolean isDefault;
+
+    public DefaultFlavor(String name) {
+        this(name, false);
+    }
+
+    public DefaultFlavor(String name, boolean isDefault) {
+        this.name = name;
+        this.isDefault = isDefault;
+    }
+
+    public String getName() {
+        return name;
+    }
+
+    public boolean isDefault() {
+        return isDefault;
+    }
+
+    @Override
+    public String toString() {
+        return String.format("flavor '%s'", name);
+    }
+
+    @Override
+    public boolean equals(Object o) {
+        if (this == o) {
+            return true;
+        }
+        if (!(o instanceof DefaultFlavor)) {
+            return false;
+        }
+
+        DefaultFlavor that = (DefaultFlavor) o;
+        return name.equals(that.name);
+
+    }
+
+    @Override
+    public int hashCode() {
+        return name.hashCode();
+    }
+}
diff --git a/subprojects/cpp/src/main/groovy/org/gradle/nativecode/base/internal/DefaultFlavorContainer.java b/subprojects/cpp/src/main/groovy/org/gradle/nativecode/base/internal/DefaultFlavorContainer.java
new file mode 100644
index 0000000..6f9df27
--- /dev/null
+++ b/subprojects/cpp/src/main/groovy/org/gradle/nativecode/base/internal/DefaultFlavorContainer.java
@@ -0,0 +1,57 @@
+/*
+ * Copyright 2013 the original author or authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.gradle.nativecode.base.internal;
+
+import org.gradle.api.internal.AbstractNamedDomainObjectContainer;
+import org.gradle.internal.reflect.Instantiator;
+import org.gradle.nativecode.base.Flavor;
+import org.gradle.nativecode.base.FlavorContainer;
+
+import java.util.Collection;
+
+public class DefaultFlavorContainer extends AbstractNamedDomainObjectContainer<Flavor> implements FlavorContainer {
+    boolean hasDefault;
+    public DefaultFlavorContainer(Instantiator instantiator) {
+        super(Flavor.class, instantiator);
+        add(Flavor.DEFAULT);
+        hasDefault = true;
+    }
+
+    @Override
+    public boolean add(Flavor o) {
+        removeDefault();
+        return super.add(o);
+    }
+
+    @Override
+    public boolean addAll(Collection<? extends Flavor> c) {
+        removeDefault();
+        return super.addAll(c);
+    }
+
+    private void removeDefault() {
+        if (hasDefault) {
+            remove(Flavor.DEFAULT);
+            hasDefault = false;
+        }
+    }
+
+    @Override
+    protected Flavor doCreate(String name) {
+        return getInstantiator().newInstance(DefaultFlavor.class, name);
+    }
+}
diff --git a/subprojects/cpp/src/main/groovy/org/gradle/nativecode/base/internal/DefaultLibrary.java b/subprojects/cpp/src/main/groovy/org/gradle/nativecode/base/internal/DefaultLibrary.java
new file mode 100755
index 0000000..9ca9cad
--- /dev/null
+++ b/subprojects/cpp/src/main/groovy/org/gradle/nativecode/base/internal/DefaultLibrary.java
@@ -0,0 +1,78 @@
+/*
+ * Copyright 2012 the original author or authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.gradle.nativecode.base.internal;
+
+import org.gradle.api.Action;
+import org.gradle.api.DomainObjectSet;
+import org.gradle.api.file.SourceDirectorySet;
+import org.gradle.api.internal.file.DefaultSourceDirectorySet;
+import org.gradle.api.internal.file.FileResolver;
+import org.gradle.internal.reflect.Instantiator;
+import org.gradle.nativecode.base.*;
+
+import java.util.ArrayList;
+import java.util.List;
+
+public class DefaultLibrary extends DefaultNativeComponent implements Library {
+    private final DefaultSourceDirectorySet headers;
+
+    public DefaultLibrary(String name, Instantiator instantiator, FileResolver fileResolver) {
+        super(name, instantiator);
+        this.headers = new DefaultSourceDirectorySet("headers", String.format("Exported headers for native library '%s'", name), fileResolver);
+        initExportedHeaderTracking();
+    }
+
+    @Override
+    public String toString() {
+        return String.format("library '%s'", getName());
+    }
+
+    public SourceDirectorySet getHeaders() {
+        return headers;
+    }
+
+    public ConfigurableLibraryResolver getShared() {
+        return new DefaultLibraryResolver(this).withType(SharedLibraryBinary.class);
+    }
+
+    public ConfigurableLibraryResolver getStatic() {
+        return new DefaultLibraryResolver(this).withType(StaticLibraryBinary.class);
+    }
+
+    private void initExportedHeaderTracking() {
+        // TODO - headers.srcDirs() should allow a Callable<SourceDirectorySet> for lazy calculation
+        final DomainObjectSet<HeaderExportingSourceSet> headerExportingSourceSets = getSource().withType(HeaderExportingSourceSet.class);
+        headerExportingSourceSets.all(new Action<HeaderExportingSourceSet>() {
+            public void execute(HeaderExportingSourceSet headerExportingSourceSet) {
+                updateHeaderDirs(headerExportingSourceSets, headers);
+            }
+        });
+        headerExportingSourceSets.whenObjectRemoved(new Action<HeaderExportingSourceSet>() {
+            public void execute(HeaderExportingSourceSet headerExportingSourceSet) {
+                updateHeaderDirs(headerExportingSourceSets, headers);
+            }
+        });
+    }
+
+    private void updateHeaderDirs(DomainObjectSet<HeaderExportingSourceSet> sourceSets, DefaultSourceDirectorySet headers) {
+        List<SourceDirectorySet> headerDirs = new ArrayList<SourceDirectorySet>();
+        for (HeaderExportingSourceSet sourceSet : sourceSets) {
+            headerDirs.add(sourceSet.getExportedHeaders());
+        }
+        headers.setSrcDirs(headerDirs);
+    }
+}
\ No newline at end of file
diff --git a/subprojects/cpp/src/main/groovy/org/gradle/nativecode/base/internal/DefaultLibraryContainer.java b/subprojects/cpp/src/main/groovy/org/gradle/nativecode/base/internal/DefaultLibraryContainer.java
new file mode 100644
index 0000000..b9fe4ae
--- /dev/null
+++ b/subprojects/cpp/src/main/groovy/org/gradle/nativecode/base/internal/DefaultLibraryContainer.java
@@ -0,0 +1,37 @@
+/*
+ * Copyright 2013 the original author or authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.gradle.nativecode.base.internal;
+
+import org.gradle.api.internal.AbstractNamedDomainObjectContainer;
+import org.gradle.api.internal.file.FileResolver;
+import org.gradle.internal.reflect.Instantiator;
+import org.gradle.nativecode.base.Library;
+import org.gradle.nativecode.base.LibraryContainer;
+
+public class DefaultLibraryContainer extends AbstractNamedDomainObjectContainer<Library> implements LibraryContainer {
+    private final FileResolver fileResolver;
+
+    public DefaultLibraryContainer(Instantiator instantiator, FileResolver fileResolver) {
+        super(Library.class, instantiator);
+        this.fileResolver = fileResolver;
+    }
+
+    @Override
+    protected Library doCreate(String name) {
+        return getInstantiator().newInstance(DefaultLibrary.class, name, getInstantiator(), fileResolver);
+    }
+}
diff --git a/subprojects/cpp/src/main/groovy/org/gradle/nativecode/base/internal/DefaultLibraryResolver.java b/subprojects/cpp/src/main/groovy/org/gradle/nativecode/base/internal/DefaultLibraryResolver.java
new file mode 100644
index 0000000..ab7d8ea
--- /dev/null
+++ b/subprojects/cpp/src/main/groovy/org/gradle/nativecode/base/internal/DefaultLibraryResolver.java
@@ -0,0 +1,56 @@
+/*
+ * Copyright 2013 the original author or authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.gradle.nativecode.base.internal;
+
+import org.gradle.api.InvalidUserDataException;
+import org.gradle.nativecode.base.*;
+
+class DefaultLibraryResolver implements ConfigurableLibraryResolver {
+    private Flavor flavor = Flavor.DEFAULT;
+    private Class<? extends LibraryBinary> type = SharedLibraryBinary.class;
+    private Library library;
+
+    public DefaultLibraryResolver(Library library) {
+        this.library = library;
+    }
+
+    public ConfigurableLibraryResolver withFlavor(Flavor flavor) {
+        this.flavor = flavor;
+        return this;
+    }
+
+    public ConfigurableLibraryResolver withType(Class<? extends LibraryBinary> type) {
+        this.type = type;
+        return this;
+    }
+
+    public NativeDependencySet resolve() {
+        for (LibraryBinary candidate : library.getBinaries().withType(type)) {
+            // If the library has only 1 flavor, then flavor is not important
+            if (library.getFlavors().size() == 1) {
+                return candidate.resolve();
+            }
+            // Otherwise match on the flavor
+            if (flavor.equals(candidate.getFlavor())) {
+                return candidate.resolve();
+            }
+        }
+
+        String typeName = type == SharedLibraryBinary.class ? "shared" : "static";
+        throw new InvalidUserDataException(String.format("No %s library binary available for %s with %s", typeName, library, flavor));
+    }
+}
diff --git a/subprojects/cpp/src/main/groovy/org/gradle/nativecode/base/internal/DefaultNativeBinary.java b/subprojects/cpp/src/main/groovy/org/gradle/nativecode/base/internal/DefaultNativeBinary.java
new file mode 100644
index 0000000..9b06538
--- /dev/null
+++ b/subprojects/cpp/src/main/groovy/org/gradle/nativecode/base/internal/DefaultNativeBinary.java
@@ -0,0 +1,150 @@
+/*
+ * Copyright 2013 the original author or authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.gradle.nativecode.base.internal;
+
+import org.gradle.api.Action;
+import org.gradle.api.DomainObjectSet;
+import org.gradle.api.internal.DefaultDomainObjectSet;
+import org.gradle.api.internal.notations.api.NotationParser;
+import org.gradle.language.base.LanguageSourceSet;
+import org.gradle.language.base.internal.AbstractBuildableModelElement;
+import org.gradle.language.base.internal.BinaryNamingScheme;
+import org.gradle.language.base.internal.DefaultBinaryNamingScheme;
+import org.gradle.nativecode.base.Flavor;
+import org.gradle.nativecode.base.NativeComponent;
+import org.gradle.nativecode.base.NativeDependencySet;
+import org.gradle.nativecode.base.tasks.BuildBinaryTask;
+
+import java.io.File;
+import java.util.*;
+
+public abstract class DefaultNativeBinary extends AbstractBuildableModelElement implements NativeBinaryInternal {
+    private final NotationParser<Set<LanguageSourceSet>> sourcesNotationParser = SourceSetNotationParser.parser();
+    private final ResolvableNativeDependencySet libs = new ResolvableNativeDependencySet();
+    private final DomainObjectSet<LanguageSourceSet> source = new DefaultDomainObjectSet<LanguageSourceSet>(LanguageSourceSet.class);
+    private final ArrayList<Object> compilerArgs = new ArrayList<Object>();
+    private final ArrayList<Object> assemblerArgs = new ArrayList<Object>();
+    private final ArrayList<Object> linkerArgs = new ArrayList<Object>();
+    private final ArrayList<Object> defines = new ArrayList<Object>();
+    private final BinaryNamingScheme namingScheme;
+    private final Flavor flavor;
+    private final ToolChainInternal toolChain;
+    private BuildBinaryTask builderTask;
+    private File outputFile;
+
+    protected DefaultNativeBinary(NativeComponent owner, Flavor flavor, ToolChainInternal toolChain, DefaultBinaryNamingScheme namingScheme) {
+        this.namingScheme = namingScheme;
+        this.flavor = flavor;
+        this.toolChain = toolChain;
+        owner.getSource().all(new Action<LanguageSourceSet>() {
+            public void execute(LanguageSourceSet sourceSet) {
+                source.add(sourceSet);
+            }
+        });
+    }
+
+    @Override
+    public String toString() {
+        return namingScheme.getDescription();
+    }
+
+    public Flavor getFlavor() {
+        return flavor;
+    }
+
+    public BuildBinaryTask getBuilderTask() {
+        return builderTask;
+    }
+
+    public void setBuilderTask(BuildBinaryTask builderTask) {
+        this.builderTask = builderTask;
+        dependsOn(builderTask);
+    }
+
+    public String getName() {
+        return namingScheme.getLifecycleTaskName();
+    }
+
+    public ToolChainInternal getToolChain() {
+        return toolChain;
+    }
+
+    public File getOutputFile() {
+        return outputFile;
+    }
+
+    public void setOutputFile(File outputFile) {
+        this.outputFile = outputFile;
+    }
+
+    public DomainObjectSet<LanguageSourceSet> getSource() {
+        return source;
+    }
+
+    public void source(Object sources) {
+        source.addAll(sourcesNotationParser.parseNotation(sources));
+    }
+
+    public List<Object> getMacros() {
+        return defines;
+    }
+
+    public void define(Object... defines) {
+        Collections.addAll(this.defines, defines);
+    }
+
+    public List<Object> getCompilerArgs() {
+        return compilerArgs;
+    }
+
+    public void compilerArgs(Object... args) {
+        Collections.addAll(compilerArgs, args);
+    }
+
+    public List<Object> getAssemblerArgs() {
+        return assemblerArgs;
+    }
+
+    public void assemblerArgs(Object... args) {
+        Collections.addAll(assemblerArgs, args);
+    }
+
+    public List<Object> getLinkerArgs() {
+        return linkerArgs;
+    }
+
+    public void linkerArgs(Object... args) {
+        Collections.addAll(linkerArgs, args);
+    }
+
+    public BinaryNamingScheme getNamingScheme() {
+        return namingScheme;
+    }
+
+    public Collection<NativeDependencySet> getLibs() {
+        return libs.resolve(this);
+    }
+
+    public void lib(Object notation) {
+        libs.add(notation);
+    }
+
+    public abstract String getOutputFileName();
+
+    protected abstract NativeComponent getComponent();
+
+}
diff --git a/subprojects/cpp/src/main/groovy/org/gradle/nativecode/base/internal/DefaultNativeComponent.java b/subprojects/cpp/src/main/groovy/org/gradle/nativecode/base/internal/DefaultNativeComponent.java
new file mode 100644
index 0000000..5ecb2d9
--- /dev/null
+++ b/subprojects/cpp/src/main/groovy/org/gradle/nativecode/base/internal/DefaultNativeComponent.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.nativecode.base.internal;
+
+import org.gradle.api.Action;
+import org.gradle.api.DomainObjectSet;
+import org.gradle.api.internal.DefaultDomainObjectSet;
+import org.gradle.api.internal.notations.api.NotationParser;
+import org.gradle.internal.reflect.Instantiator;
+import org.gradle.language.base.LanguageSourceSet;
+import org.gradle.nativecode.base.FlavorContainer;
+import org.gradle.nativecode.base.NativeBinary;
+import org.gradle.nativecode.base.NativeComponent;
+import org.gradle.util.GUtil;
+
+import java.util.Set;
+
+public class DefaultNativeComponent implements NativeComponent {
+    private final NotationParser<Set<LanguageSourceSet>> sourcesNotationParser = SourceSetNotationParser.parser();
+    private final String name;
+    private final DomainObjectSet<LanguageSourceSet> sourceSets;
+    private final DefaultDomainObjectSet<NativeBinary> binaries;
+    private final DefaultFlavorContainer flavors;
+    private String baseName;
+
+    public DefaultNativeComponent(String name, Instantiator instantiator) {
+        this.name = name;
+        this.sourceSets = new DefaultDomainObjectSet<LanguageSourceSet>(LanguageSourceSet.class);
+        binaries = new DefaultDomainObjectSet<NativeBinary>(NativeBinary.class);
+        flavors = instantiator.newInstance(DefaultFlavorContainer.class, instantiator);
+    }
+
+    public String getName() {
+        return name;
+    }
+
+    public DomainObjectSet<LanguageSourceSet> getSource() {
+        return sourceSets;
+    }
+
+    public void source(Object sources) {
+        sourceSets.addAll(sourcesNotationParser.parseNotation(sources));
+    }
+
+    public DomainObjectSet<NativeBinary> getBinaries() {
+        return binaries;
+    }
+
+    public String getBaseName() {
+        return GUtil.elvis(baseName, name);
+    }
+
+    public void setBaseName(String baseName) {
+        this.baseName = baseName;
+    }
+
+    public FlavorContainer getFlavors() {
+        return flavors;
+    }
+
+    public void flavors(Action<FlavorContainer> config) {
+        config.execute(flavors);
+    }
+}
\ No newline at end of file
diff --git a/subprojects/cpp/src/main/groovy/org/gradle/nativecode/base/internal/DefaultSharedLibraryBinary.java b/subprojects/cpp/src/main/groovy/org/gradle/nativecode/base/internal/DefaultSharedLibraryBinary.java
new file mode 100644
index 0000000..ce8dcd6
--- /dev/null
+++ b/subprojects/cpp/src/main/groovy/org/gradle/nativecode/base/internal/DefaultSharedLibraryBinary.java
@@ -0,0 +1,86 @@
+/*
+ * Copyright 2013 the original author or authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.gradle.nativecode.base.internal;
+
+import org.gradle.api.Buildable;
+import org.gradle.api.file.FileCollection;
+import org.gradle.api.internal.file.collections.FileCollectionAdapter;
+import org.gradle.api.internal.file.collections.MinimalFileSet;
+import org.gradle.api.tasks.TaskDependency;
+import org.gradle.language.base.internal.DefaultBinaryNamingScheme;
+import org.gradle.nativecode.base.*;
+
+import java.io.File;
+import java.util.Collections;
+import java.util.Set;
+
+public class DefaultSharedLibraryBinary extends DefaultNativeBinary implements SharedLibraryBinary {
+    private final Library library;
+
+    public DefaultSharedLibraryBinary(Library library, Flavor flavor, ToolChainInternal toolChain, DefaultBinaryNamingScheme namingScheme) {
+        super(library, flavor, toolChain, namingScheme.withTypeString("SharedLibrary"));
+        this.library = library;
+    }
+
+    public Library getComponent() {
+        return library;
+    }
+
+    public String getOutputFileName() {
+        return getToolChain().getSharedLibraryName(getComponent().getBaseName());
+    }
+
+    private File getLinkFile() {
+        return new File(getToolChain().getSharedLibraryLinkFileName(getOutputFile().getPath()));
+    }
+
+    public NativeDependencySet resolve() {
+        return new NativeDependencySet() {
+            public FileCollection getIncludeRoots() {
+                return library.getHeaders();
+            }
+
+            public FileCollection getLinkFiles() {
+                return new FileCollectionAdapter(new RuntimeFiles(getLinkFile()));
+            }
+
+            public FileCollection getRuntimeFiles() {
+                return new FileCollectionAdapter(new RuntimeFiles(getOutputFile()));
+            }
+        };
+    }
+
+    private class RuntimeFiles implements MinimalFileSet, Buildable {
+        private final File outputFile;
+
+        private RuntimeFiles(File outputFile) {
+            this.outputFile = outputFile;
+        }
+
+        public Set<File> getFiles() {
+            return Collections.singleton(outputFile);
+        }
+
+        public String getDisplayName() {
+            return DefaultSharedLibraryBinary.this.toString();
+        }
+
+        public TaskDependency getBuildDependencies() {
+            return DefaultSharedLibraryBinary.this.getBuildDependencies();
+        }
+    }
+}
diff --git a/subprojects/cpp/src/main/groovy/org/gradle/nativecode/base/internal/DefaultStaticLibraryBinary.java b/subprojects/cpp/src/main/groovy/org/gradle/nativecode/base/internal/DefaultStaticLibraryBinary.java
new file mode 100644
index 0000000..346fd3b
--- /dev/null
+++ b/subprojects/cpp/src/main/groovy/org/gradle/nativecode/base/internal/DefaultStaticLibraryBinary.java
@@ -0,0 +1,96 @@
+/*
+ * Copyright 2013 the original author or authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.gradle.nativecode.base.internal;
+
+import org.gradle.api.Buildable;
+import org.gradle.api.file.FileCollection;
+import org.gradle.api.internal.file.collections.FileCollectionAdapter;
+import org.gradle.api.internal.file.collections.MinimalFileSet;
+import org.gradle.api.internal.file.collections.SimpleFileCollection;
+import org.gradle.api.tasks.TaskDependency;
+import org.gradle.language.base.internal.DefaultBinaryNamingScheme;
+import org.gradle.nativecode.base.Flavor;
+import org.gradle.nativecode.base.Library;
+import org.gradle.nativecode.base.NativeDependencySet;
+import org.gradle.nativecode.base.StaticLibraryBinary;
+
+import java.io.File;
+import java.util.ArrayList;
+import java.util.Collections;
+import java.util.List;
+import java.util.Set;
+
+public class DefaultStaticLibraryBinary extends DefaultNativeBinary implements StaticLibraryBinary {
+    private final Library library;
+    private final ArrayList<Object> staticLibArgs = new ArrayList<Object>();
+
+    public DefaultStaticLibraryBinary(Library library, Flavor flavor, ToolChainInternal toolChain, DefaultBinaryNamingScheme namingScheme) {
+        super(library, flavor, toolChain, namingScheme.withTypeString("StaticLibrary"));
+        this.library = library;
+    }
+
+    public Library getComponent() {
+        return library;
+    }
+
+    public String getOutputFileName() {
+        return getToolChain().getStaticLibraryName(getComponent().getBaseName());
+    }
+
+    public List<Object> getStaticLibArgs() {
+        return staticLibArgs;
+    }
+
+    public void staticLibArgs(Object... args) {
+        Collections.addAll(staticLibArgs, args);
+    }
+
+    public NativeDependencySet resolve() {
+        return new NativeDependencySet() {
+            public FileCollection getIncludeRoots() {
+                return library.getHeaders();
+            }
+
+            public FileCollection getLinkFiles() {
+                return new FileCollectionAdapter(new LibraryFile());
+            }
+
+            public FileCollection getRuntimeFiles() {
+                return new SimpleFileCollection() {
+                    @Override
+                    public String getDisplayName() {
+                        return DefaultStaticLibraryBinary.this.toString();
+                    }
+                };
+            }
+        };
+    }
+
+    private class LibraryFile implements MinimalFileSet, Buildable {
+        public Set<File> getFiles() {
+            return Collections.singleton(getOutputFile());
+        }
+
+        public String getDisplayName() {
+            return DefaultStaticLibraryBinary.this.toString();
+        }
+
+        public TaskDependency getBuildDependencies() {
+            return DefaultStaticLibraryBinary.this.getBuildDependencies();
+        }
+    }
+}
diff --git a/subprojects/cpp/src/main/groovy/org/gradle/nativecode/base/internal/DefaultToolChainRegistry.java b/subprojects/cpp/src/main/groovy/org/gradle/nativecode/base/internal/DefaultToolChainRegistry.java
new file mode 100755
index 0000000..3ab1d4c
--- /dev/null
+++ b/subprojects/cpp/src/main/groovy/org/gradle/nativecode/base/internal/DefaultToolChainRegistry.java
@@ -0,0 +1,164 @@
+/*
+ * Copyright 2011 the original author or authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.gradle.nativecode.base.internal;
+
+import groovy.lang.Closure;
+import org.gradle.api.InvalidUserDataException;
+import org.gradle.api.internal.AbstractNamedDomainObjectContainer;
+import org.gradle.api.internal.DefaultPolymorphicDomainObjectContainer;
+import org.gradle.api.internal.tasks.compile.Compiler;
+import org.gradle.internal.os.OperatingSystem;
+import org.gradle.internal.reflect.Instantiator;
+import org.gradle.nativecode.base.ToolChain;
+import org.gradle.nativecode.base.ToolChainRegistry;
+
+import java.util.ArrayList;
+import java.util.Collection;
+import java.util.List;
+
+// TODO:DAZ Need a better way of having a container with defaults. Similar for FlavorContainer.
+// Probably need a separate container for defaults and registered, and then have a simple delegating container, or factory.
+public class DefaultToolChainRegistry extends DefaultPolymorphicDomainObjectContainer<ToolChain> implements ToolChainRegistry {
+    private ToolChain defaultToolChain;
+
+    public DefaultToolChainRegistry(Instantiator instantiator) {
+        super(ToolChain.class, instantiator);
+    }
+
+    @Override
+    protected void handleAttemptToAddItemWithNonUniqueName(ToolChain toolChain) {
+        throw new InvalidUserDataException(String.format("ToolChain with name '%s' added multiple times", toolChain.getName()));
+    }
+
+    public void registerDefaultToolChain(String name, Class<? extends ToolChain> type) {
+        if (isEmpty()) {
+            assertCanAdd(name);
+            ToolChain added = doCreate(name, type);
+            ToolChainInternal candidate = (ToolChainInternal) added;
+            if (candidate.getAvailability().isAvailable()) {
+                add(candidate);
+            }
+            defaultToolChain = candidate;
+        }
+    }
+
+    @Override
+    public boolean add(ToolChain o) {
+        removeDefault();
+        return super.add(o);
+    }
+
+    @Override
+    public boolean addAll(Collection<? extends ToolChain> c) {
+        removeDefault();
+        return super.addAll(c);
+    }
+
+    @Override
+    public AbstractNamedDomainObjectContainer<ToolChain> configure(Closure configureClosure) {
+        removeDefault();
+        return super.configure(configureClosure);
+    }
+
+    private void removeDefault() {
+        if (defaultToolChain != null) {
+            remove(defaultToolChain);
+            defaultToolChain = null;
+        }
+    }
+
+    public List<ToolChainInternal> getAvailableToolChains() {
+        List<ToolChainInternal> availableToolChains = new ArrayList<ToolChainInternal>();
+        List<String> messages = new ArrayList<String>();
+        for (ToolChainInternal toolChain : this.withType(ToolChainInternal.class)) {
+            ToolChainAvailability availability = toolChain.getAvailability();
+            if (availability.isAvailable()) {
+                availableToolChains.add(toolChain);
+            }
+            messages.add(String.format("Could not load '%s': %s", toolChain.getName(), availability.getUnavailableMessage()));
+        }
+        if (availableToolChains.isEmpty()) {
+            availableToolChains.add(new UnavailableToolChain(messages));
+        }
+        return availableToolChains;
+    }
+
+    public ToolChainInternal getDefaultToolChain() {
+        return getAvailableToolChains().get(0);
+    }
+
+    private static class UnavailableToolChain implements ToolChainInternal {
+        private final List<String> messages;
+        private final OperatingSystem operatingSystem = OperatingSystem.current();
+
+        public UnavailableToolChain(List<String> messages) {
+            this.messages = messages;
+        }
+
+        public String getName() {
+            return "unavailable";
+        }
+
+        private IllegalStateException failure() {
+            return new IllegalStateException(String.format("No tool chain is available: %s", messages));
+        }
+
+        public <T extends BinaryToolSpec> Compiler<T> createCppCompiler() {
+            throw failure();
+        }
+
+        public <T extends BinaryToolSpec> Compiler<T> createCCompiler() {
+            throw failure();
+        }
+
+        public <T extends BinaryToolSpec> Compiler<T> createAssembler() {
+            throw failure();
+        }
+
+        public <T extends LinkerSpec> Compiler<T> createLinker() {
+            throw failure();
+        }
+
+        public <T extends StaticLibraryArchiverSpec> Compiler<T> createStaticLibraryArchiver() {
+            throw failure();
+        }
+
+        public ToolChainAvailability getAvailability() {
+            return new ToolChainAvailability().unavailable("No tool chain is available.");
+        }
+
+        public String getExecutableName(String executablePath) {
+            return operatingSystem.getExecutableName(executablePath);
+        }
+
+        public String getSharedLibraryName(String libraryName) {
+            return operatingSystem.getSharedLibraryName(libraryName);
+        }
+
+        public String getSharedLibraryLinkFileName(String libraryName) {
+            return getSharedLibraryName(libraryName);
+        }
+
+        public String getStaticLibraryName(String libraryName) {
+            return operatingSystem.getStaticLibraryName(libraryName);
+        }
+
+        public String getOutputType() {
+            throw failure();
+        }
+
+    }
+}
\ No newline at end of file
diff --git a/subprojects/cpp/src/main/groovy/org/gradle/nativecode/base/internal/ExecutableLinkerSpec.java b/subprojects/cpp/src/main/groovy/org/gradle/nativecode/base/internal/ExecutableLinkerSpec.java
new file mode 100644
index 0000000..f046c1e
--- /dev/null
+++ b/subprojects/cpp/src/main/groovy/org/gradle/nativecode/base/internal/ExecutableLinkerSpec.java
@@ -0,0 +1,20 @@
+/*
+ * Copyright 2013 the original author or authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.gradle.nativecode.base.internal;
+
+public interface ExecutableLinkerSpec extends LinkerSpec {
+}
diff --git a/subprojects/cpp/src/main/groovy/org/gradle/nativecode/base/internal/LinkerSpec.java b/subprojects/cpp/src/main/groovy/org/gradle/nativecode/base/internal/LinkerSpec.java
new file mode 100644
index 0000000..240750f
--- /dev/null
+++ b/subprojects/cpp/src/main/groovy/org/gradle/nativecode/base/internal/LinkerSpec.java
@@ -0,0 +1,42 @@
+/*
+ * Copyright 2013 the original author or authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.gradle.nativecode.base.internal;
+
+import java.io.File;
+
+/**
+ * A high level interface to the compiler, specifying what is to be compiled and how.
+ */
+public interface LinkerSpec extends BinaryToolSpec {
+
+    Iterable<File> getSource();
+
+    void setSource(Iterable<File> source);
+
+    Iterable<File> getLibs();
+
+    void setLibs(Iterable<File> libs);
+
+    Iterable<String> getArgs();
+
+    void setArgs(Iterable<String> args);
+
+    File getOutputFile();
+
+    void setOutputFile(File outputFile);
+
+}
diff --git a/subprojects/cpp/src/main/groovy/org/gradle/nativecode/base/internal/NativeBinaryFactory.java b/subprojects/cpp/src/main/groovy/org/gradle/nativecode/base/internal/NativeBinaryFactory.java
new file mode 100644
index 0000000..06ab153
--- /dev/null
+++ b/subprojects/cpp/src/main/groovy/org/gradle/nativecode/base/internal/NativeBinaryFactory.java
@@ -0,0 +1,63 @@
+/*
+ * Copyright 2013 the original author or authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.gradle.nativecode.base.internal;
+
+import org.gradle.api.Project;
+import org.gradle.internal.reflect.Instantiator;
+import org.gradle.language.base.internal.DefaultBinaryNamingScheme;
+import org.gradle.nativecode.base.Flavor;
+import org.gradle.nativecode.base.NativeComponent;
+import org.gradle.nativecode.base.ToolChain;
+
+import java.io.File;
+import java.util.Collection;
+
+class NativeBinaryFactory {
+    private final Instantiator instantiator;
+    private final Project project;
+    private final boolean useToolChainDimension;
+
+    public NativeBinaryFactory(Instantiator instantiator, Project project, Collection<? extends ToolChain> allToolChains) {
+        this.instantiator = instantiator;
+        this.project = project;
+        this.useToolChainDimension = allToolChains.size() > 1;
+    }
+
+    public <T extends DefaultNativeBinary> T createNativeBinary(Class<T> type, NativeComponent component, ToolChain toolChain, Flavor flavor) {
+        DefaultBinaryNamingScheme namingScheme = createNamingScheme(component, useToolChainDimension, toolChain, flavor);
+        T nativeBinary = instantiator.newInstance(type, component, flavor, toolChain, namingScheme);
+        setupDefaults(project, nativeBinary);
+        component.getBinaries().add(nativeBinary);
+        return nativeBinary;
+    }
+
+    private DefaultBinaryNamingScheme createNamingScheme(NativeComponent component, boolean useToolChainDimension, ToolChain toolChain, Flavor flavor) {
+        DefaultBinaryNamingScheme namingScheme = new DefaultBinaryNamingScheme(component.getName());
+        if (useToolChainDimension) {
+            namingScheme = namingScheme.withVariantDimension(toolChain.getName());
+        }
+        if (component.getFlavors().size() > 1) {
+            namingScheme = namingScheme.withVariantDimension(flavor.getName());
+        }
+        return namingScheme;
+    }
+
+
+    private void setupDefaults(Project project, DefaultNativeBinary nativeBinary) {
+        nativeBinary.setOutputFile(new File(project.getBuildDir(), "binaries/" + nativeBinary.getNamingScheme().getOutputDirectoryBase() + "/" + nativeBinary.getOutputFileName()));
+    }
+}
diff --git a/subprojects/cpp/src/main/groovy/org/gradle/nativecode/base/internal/NativeBinaryInternal.java b/subprojects/cpp/src/main/groovy/org/gradle/nativecode/base/internal/NativeBinaryInternal.java
new file mode 100644
index 0000000..f294c1e
--- /dev/null
+++ b/subprojects/cpp/src/main/groovy/org/gradle/nativecode/base/internal/NativeBinaryInternal.java
@@ -0,0 +1,27 @@
+/*
+ * Copyright 2013 the original author or authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.gradle.nativecode.base.internal;
+
+import org.gradle.language.base.internal.BinaryInternal;
+import org.gradle.nativecode.base.NativeBinary;
+import org.gradle.nativecode.base.tasks.BuildBinaryTask;
+
+public interface NativeBinaryInternal extends NativeBinary, BinaryInternal {
+    // TODO:DAZ Develop this concept further, so we can do eg. 'executable.tasks.link'
+    void setBuilderTask(BuildBinaryTask task);
+    BuildBinaryTask getBuilderTask();
+}
diff --git a/subprojects/cpp/src/main/groovy/org/gradle/nativecode/base/internal/ResolvableNativeDependencySet.java b/subprojects/cpp/src/main/groovy/org/gradle/nativecode/base/internal/ResolvableNativeDependencySet.java
new file mode 100644
index 0000000..f5253f4
--- /dev/null
+++ b/subprojects/cpp/src/main/groovy/org/gradle/nativecode/base/internal/ResolvableNativeDependencySet.java
@@ -0,0 +1,61 @@
+/*
+ * Copyright 2013 the original author or authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.gradle.nativecode.base.internal;
+
+import org.gradle.api.InvalidUserDataException;
+import org.gradle.nativecode.base.Library;
+import org.gradle.nativecode.base.LibraryResolver;
+import org.gradle.nativecode.base.NativeBinary;
+import org.gradle.nativecode.base.NativeDependencySet;
+
+import java.util.ArrayList;
+import java.util.Collection;
+import java.util.List;
+
+public class ResolvableNativeDependencySet {
+    private final List<Object> libs = new ArrayList<Object>();
+
+    public void add(Object lib) {
+        this.libs.add(lib);
+    }
+
+    public Collection<NativeDependencySet> resolve(NativeBinary target) {
+        List<NativeDependencySet> result = new ArrayList<NativeDependencySet>();
+        for (Object lib : libs) {
+            result.add(resolve(target, lib));
+            resolve(target, lib);
+        }
+        return result;
+    }
+
+    private NativeDependencySet resolve(NativeBinary target, Object lib) {
+        if (lib instanceof NativeDependencySet) {
+            return (NativeDependencySet) lib;
+        }
+        if (lib instanceof Library) {
+            return resolve(target, ((Library) lib).getShared());
+        }
+        if (lib instanceof ConfigurableLibraryResolver) {
+            return ((ConfigurableLibraryResolver) lib).withFlavor(target.getFlavor()).resolve();
+        }
+        if (lib instanceof LibraryResolver) {
+            return ((LibraryResolver) lib).resolve();
+        }
+
+        throw new InvalidUserDataException("Not a valid type for a library dependency: " + lib);
+    }
+}
diff --git a/subprojects/cpp/src/main/groovy/org/gradle/nativecode/base/internal/SharedLibraryLinkerSpec.java b/subprojects/cpp/src/main/groovy/org/gradle/nativecode/base/internal/SharedLibraryLinkerSpec.java
new file mode 100644
index 0000000..9a673cd
--- /dev/null
+++ b/subprojects/cpp/src/main/groovy/org/gradle/nativecode/base/internal/SharedLibraryLinkerSpec.java
@@ -0,0 +1,23 @@
+/*
+ * Copyright 2013 the original author or authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.gradle.nativecode.base.internal;
+
+public interface SharedLibraryLinkerSpec extends LinkerSpec {
+    String getInstallName();
+
+    void setInstallName(String path);
+}
diff --git a/subprojects/cpp/src/main/groovy/org/gradle/nativecode/base/internal/SourceSetNotationParser.java b/subprojects/cpp/src/main/groovy/org/gradle/nativecode/base/internal/SourceSetNotationParser.java
new file mode 100644
index 0000000..c6a68bb
--- /dev/null
+++ b/subprojects/cpp/src/main/groovy/org/gradle/nativecode/base/internal/SourceSetNotationParser.java
@@ -0,0 +1,73 @@
+/*
+ * Copyright 2013 the original author or authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.gradle.nativecode.base.internal;
+
+import org.gradle.api.internal.notations.NotationParserBuilder;
+import org.gradle.api.internal.notations.TypeInfo;
+import org.gradle.api.internal.notations.api.NotationParser;
+import org.gradle.api.internal.notations.parsers.TypedNotationParser;
+import org.gradle.language.base.FunctionalSourceSet;
+import org.gradle.language.base.LanguageSourceSet;
+
+import java.util.Collection;
+import java.util.Collections;
+import java.util.HashSet;
+import java.util.Set;
+
+public class SourceSetNotationParser {
+    public static NotationParser<Set<LanguageSourceSet>> parser() {
+        return new NotationParserBuilder<Set<LanguageSourceSet>>()
+                .resultingType(new TypeInfo<Set<LanguageSourceSet>>(Set.class))
+                .parser(new FunctionalSourceSetConverter())
+                .parser(new SingleLanguageSourceSetConverter())
+                .parser(new LanguageSourceSetCollectionConverter())
+                .toComposite();
+    }
+
+    private static class FunctionalSourceSetConverter extends TypedNotationParser<FunctionalSourceSet, Set<LanguageSourceSet>> {
+        private FunctionalSourceSetConverter() {
+            super(FunctionalSourceSet.class);
+        }
+
+        @Override
+        protected Set<LanguageSourceSet> parseType(FunctionalSourceSet notation) {
+            return notation;
+        }
+    }
+
+    private static class SingleLanguageSourceSetConverter extends TypedNotationParser<LanguageSourceSet, Set<LanguageSourceSet>> {
+        private SingleLanguageSourceSetConverter() {
+            super(LanguageSourceSet.class);
+        }
+
+        @Override
+        protected Set<LanguageSourceSet> parseType(LanguageSourceSet notation) {
+            return Collections.singleton(notation);
+        }
+    }
+
+    private static class LanguageSourceSetCollectionConverter extends TypedNotationParser<Collection<LanguageSourceSet>, Set<LanguageSourceSet>> {
+        private LanguageSourceSetCollectionConverter() {
+            super(new TypeInfo<Collection<LanguageSourceSet>>(Collection.class));
+        }
+
+        @Override
+        protected Set<LanguageSourceSet> parseType(Collection<LanguageSourceSet> notation) {
+            return new HashSet<LanguageSourceSet>(notation);
+        }
+    }
+}
diff --git a/subprojects/cpp/src/main/groovy/org/gradle/nativecode/base/internal/StaticLibraryArchiverSpec.java b/subprojects/cpp/src/main/groovy/org/gradle/nativecode/base/internal/StaticLibraryArchiverSpec.java
new file mode 100644
index 0000000..051ca97
--- /dev/null
+++ b/subprojects/cpp/src/main/groovy/org/gradle/nativecode/base/internal/StaticLibraryArchiverSpec.java
@@ -0,0 +1,34 @@
+/*
+ * Copyright 2013 the original author or authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.gradle.nativecode.base.internal;
+
+import java.io.File;
+
+public interface StaticLibraryArchiverSpec extends BinaryToolSpec {
+
+    Iterable<File> getSource();
+
+    void setSource(Iterable<File> source);
+
+    File getOutputFile();
+
+    void setOutputFile(File outputFile);
+
+    Iterable<String> getArgs();
+
+    void setArgs(Iterable<String> args);
+}
diff --git a/subprojects/cpp/src/main/groovy/org/gradle/nativecode/base/internal/ToolChainAvailability.java b/subprojects/cpp/src/main/groovy/org/gradle/nativecode/base/internal/ToolChainAvailability.java
new file mode 100644
index 0000000..0cc344c
--- /dev/null
+++ b/subprojects/cpp/src/main/groovy/org/gradle/nativecode/base/internal/ToolChainAvailability.java
@@ -0,0 +1,48 @@
+/*
+ * Copyright 2013 the original author or authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.gradle.nativecode.base.internal;
+
+import java.io.File;
+
+public class ToolChainAvailability {
+    private String unavailableMessage;
+
+    public ToolChainAvailability() {
+        unavailableMessage = null;
+    }
+
+    public boolean isAvailable() {
+        return unavailableMessage == null;
+    }
+
+    public String getUnavailableMessage() {
+        return unavailableMessage;
+    }
+
+    public void mustExist(String toolName, File tool) {
+        if (this.unavailableMessage == null && (tool == null || !tool.exists())) {
+            this.unavailableMessage = String.format("%s cannot be found", toolName);
+        }
+    }
+
+    public ToolChainAvailability unavailable(String unavailableMessage) {
+        if (this.unavailableMessage == null) {
+            this.unavailableMessage = unavailableMessage;
+        }
+        return this;
+    }
+}
diff --git a/subprojects/cpp/src/main/groovy/org/gradle/nativecode/base/internal/ToolChainInternal.java b/subprojects/cpp/src/main/groovy/org/gradle/nativecode/base/internal/ToolChainInternal.java
new file mode 100644
index 0000000..0f9456a
--- /dev/null
+++ b/subprojects/cpp/src/main/groovy/org/gradle/nativecode/base/internal/ToolChainInternal.java
@@ -0,0 +1,47 @@
+/*
+ * Copyright 2013 the original author or authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.gradle.nativecode.base.internal;
+
+import org.gradle.api.internal.tasks.compile.Compiler;
+import org.gradle.nativecode.base.ToolChain;
+
+public interface ToolChainInternal extends ToolChain {
+    ToolChainAvailability getAvailability();
+
+    <T extends BinaryToolSpec> Compiler<T> createCppCompiler();
+
+    <T extends BinaryToolSpec> Compiler<T> createCCompiler();
+
+    <T extends BinaryToolSpec> Compiler<T> createAssembler();
+
+    <T extends LinkerSpec> Compiler<T> createLinker();
+
+    <T extends StaticLibraryArchiverSpec> Compiler<T> createStaticLibraryArchiver();
+
+    String getExecutableName(String executablePath);
+
+    String getSharedLibraryName(String libraryPath);
+
+    String getSharedLibraryLinkFileName(String libraryPath);
+
+    String getStaticLibraryName(String libraryPath);
+
+    /**
+     * Returns a unique identifier for the output produced by this toolchain on the current platform.
+     */
+    String getOutputType();
+}
diff --git a/subprojects/cpp/src/main/groovy/org/gradle/nativecode/base/package-info.java b/subprojects/cpp/src/main/groovy/org/gradle/nativecode/base/package-info.java
new file mode 100644
index 0000000..6f1dc97
--- /dev/null
+++ b/subprojects/cpp/src/main/groovy/org/gradle/nativecode/base/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 that model aspects of native component projects.
+ */
+package org.gradle.nativecode.base;
diff --git a/subprojects/cpp/src/main/groovy/org/gradle/nativecode/base/plugins/NativeBinariesModelPlugin.java b/subprojects/cpp/src/main/groovy/org/gradle/nativecode/base/plugins/NativeBinariesModelPlugin.java
new file mode 100644
index 0000000..fb862d7
--- /dev/null
+++ b/subprojects/cpp/src/main/groovy/org/gradle/nativecode/base/plugins/NativeBinariesModelPlugin.java
@@ -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.nativecode.base.plugins;
+
+import org.gradle.api.*;
+import org.gradle.api.internal.file.FileResolver;
+import org.gradle.api.plugins.BasePlugin;
+import org.gradle.configuration.project.ProjectConfigurationActionContainer;
+import org.gradle.internal.reflect.Instantiator;
+import org.gradle.language.base.plugins.LanguageBasePlugin;
+import org.gradle.nativecode.base.internal.*;
+
+import javax.inject.Inject;
+
+/**
+ * A plugin that sets up the infrastructure for defining native binaries.
+ */
+ at Incubating
+public class NativeBinariesModelPlugin implements Plugin<Project> {
+    private final Instantiator instantiator;
+    private final ProjectConfigurationActionContainer configurationActions;
+    private final FileResolver fileResolver;
+
+    @Inject
+    public NativeBinariesModelPlugin(Instantiator instantiator, ProjectConfigurationActionContainer configurationActions, FileResolver fileResolver) {
+        this.instantiator = instantiator;
+        this.configurationActions = configurationActions;
+        this.fileResolver = fileResolver;
+    }
+
+    public void apply(final Project project) {
+        project.getPlugins().apply(BasePlugin.class);
+        project.getPlugins().apply(LanguageBasePlugin.class);
+
+        project.getExtensions().create("toolChains",
+                DefaultToolChainRegistry.class,
+                instantiator
+        );
+        project.getExtensions().create(
+                "executables",
+                DefaultExecutableContainer.class,
+                instantiator
+        );
+
+        project.getExtensions().create(
+                "libraries",
+                DefaultLibraryContainer.class,
+                instantiator,
+                fileResolver
+        );
+
+        configurationActions.add(new CreateNativeBinariesAction(instantiator));
+    }
+
+}
\ No newline at end of file
diff --git a/subprojects/cpp/src/main/groovy/org/gradle/nativecode/base/plugins/NativeBinariesPlugin.groovy b/subprojects/cpp/src/main/groovy/org/gradle/nativecode/base/plugins/NativeBinariesPlugin.groovy
new file mode 100644
index 0000000..f781e95
--- /dev/null
+++ b/subprojects/cpp/src/main/groovy/org/gradle/nativecode/base/plugins/NativeBinariesPlugin.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.nativecode.base.plugins
+
+import org.gradle.api.Incubating
+import org.gradle.api.Plugin
+import org.gradle.api.Project
+import org.gradle.api.internal.project.ProjectInternal
+import org.gradle.api.plugins.BasePlugin
+import org.gradle.language.base.BinaryContainer
+import org.gradle.nativecode.base.*
+import org.gradle.nativecode.base.internal.NativeBinaryInternal
+import org.gradle.nativecode.base.tasks.*
+
+/**
+ * A plugin that creates tasks used for constructing native binaries.
+ */
+ at Incubating
+public class NativeBinariesPlugin implements Plugin<Project> {
+
+    public void apply(final Project project) {
+        project.getPlugins().apply(NativeBinariesModelPlugin.class);
+        final BinaryContainer binaries = project.getExtensions().getByType(BinaryContainer.class);
+
+        binaries.withType(NativeBinary) { NativeBinaryInternal binary ->
+            bindSourceSetLibsToBinary(binary)
+            createTasks(project, binary)
+        }
+    }
+
+    private static void bindSourceSetLibsToBinary(NativeBinaryInternal binary) {
+        // TODO:DAZ Move this logic into NativeBinary (once we have laziness sorted)
+        binary.source.withType(DependentSourceSet).all { DependentSourceSet sourceSet ->
+            sourceSet.libs.each { lib ->
+                binary.lib lib
+            }
+        }
+    }
+
+    def createTasks(ProjectInternal project, NativeBinaryInternal binary) {
+        BuildBinaryTask buildBinaryTask
+        if (binary instanceof StaticLibraryBinary) {
+            buildBinaryTask = createStaticLibraryTask(project, binary)
+        } else {
+            buildBinaryTask = createLinkTask(project, binary)
+        }
+        binary.builderTask = buildBinaryTask
+
+        if (binary instanceof ExecutableBinary) {
+            createInstallTask(project, (NativeBinaryInternal) binary);
+        }
+    }
+
+    private AbstractLinkTask createLinkTask(ProjectInternal project, NativeBinaryInternal binary) {
+        AbstractLinkTask linkTask = project.task(binary.namingScheme.getTaskName("link"), type: linkTaskType(binary)) {
+             description = "Links ${binary}"
+             group = BasePlugin.BUILD_GROUP
+         }
+
+        linkTask.toolChain = binary.toolChain
+
+        binary.libs.each { NativeDependencySet lib ->
+            linkTask.lib lib.linkFiles
+        }
+
+        linkTask.conventionMapping.outputFile = { binary.outputFile }
+        linkTask.conventionMapping.linkerArgs = { binary.linkerArgs }
+        return linkTask
+    }
+
+    private static Class<? extends AbstractLinkTask> linkTaskType(NativeBinary binary) {
+        if (binary instanceof SharedLibraryBinary) {
+            return LinkSharedLibrary
+        }
+        return LinkExecutable
+    }
+
+    private CreateStaticLibrary createStaticLibraryTask(ProjectInternal project, StaticLibraryBinary binary) {
+        def namingScheme = ((NativeBinaryInternal) binary).namingScheme
+        CreateStaticLibrary task = project.task(namingScheme.getTaskName("create"), type: CreateStaticLibrary) {
+             description = "Creates ${binary}"
+             group = BasePlugin.BUILD_GROUP
+         }
+
+        task.toolChain = binary.toolChain
+        task.conventionMapping.outputFile = { binary.outputFile }
+        task.conventionMapping.staticLibArgs = { binary.staticLibArgs }
+        return task
+    }
+
+    def createInstallTask(ProjectInternal project, NativeBinaryInternal executable) {
+        InstallExecutable installTask = project.task(executable.namingScheme.getTaskName("install"), type: InstallExecutable) {
+            description = "Installs a development image of $executable"
+            group = BasePlugin.BUILD_GROUP
+        }
+
+        installTask.toolChain = executable.toolChain
+        installTask.conventionMapping.destinationDir = { project.file("${project.buildDir}/install/$executable.namingScheme.outputDirectoryBase") }
+
+        installTask.conventionMapping.executable = { executable.outputFile }
+        installTask.lib { executable.libs*.runtimeFiles }
+
+        installTask.dependsOn(executable)
+    }
+}
\ No newline at end of file
diff --git a/subprojects/cpp/src/main/groovy/org/gradle/nativecode/base/plugins/package-info.java b/subprojects/cpp/src/main/groovy/org/gradle/nativecode/base/plugins/package-info.java
new file mode 100644
index 0000000..914053b
--- /dev/null
+++ b/subprojects/cpp/src/main/groovy/org/gradle/nativecode/base/plugins/package-info.java
@@ -0,0 +1,20 @@
+/*
+ * Copyright 2013 the original author or authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+/**
+ * Plugins for building native component projects.
+ */
+package org.gradle.nativecode.base.plugins;
diff --git a/subprojects/cpp/src/main/groovy/org/gradle/nativecode/base/tasks/AbstractLinkTask.groovy b/subprojects/cpp/src/main/groovy/org/gradle/nativecode/base/tasks/AbstractLinkTask.groovy
new file mode 100644
index 0000000..4af99c9
--- /dev/null
+++ b/subprojects/cpp/src/main/groovy/org/gradle/nativecode/base/tasks/AbstractLinkTask.groovy
@@ -0,0 +1,117 @@
+/*
+ * Copyright 2013 the original author or authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.gradle.nativecode.base.tasks
+
+import org.gradle.api.DefaultTask
+import org.gradle.api.Incubating
+import org.gradle.api.file.FileCollection
+import org.gradle.api.tasks.*
+import org.gradle.language.jvm.internal.SimpleStaleClassCleaner
+import org.gradle.nativecode.base.ToolChain
+import org.gradle.nativecode.base.internal.LinkerSpec
+
+import javax.inject.Inject
+
+ at Incubating
+abstract class AbstractLinkTask extends DefaultTask implements BuildBinaryTask {
+    private FileCollection source
+
+    @Inject
+    AbstractLinkTask() {
+        libs = project.files()
+        source = project.files()
+    }
+
+    /**
+     * The tool chain used for linking.
+     */
+    ToolChain toolChain
+
+    // Invalidate output when the tool chain output changes
+    @Input
+    def getOutputType() {
+        return toolChain.outputType
+    }
+
+    // To pick up auxiliary files produced alongside the main output file
+    @OutputDirectory
+    File getDestinationDir() {
+        return getOutputFile().parentFile
+    }
+
+    /**
+     * The file where the linked binary will be located.
+     */
+    @OutputFile
+    File outputFile
+
+    /**
+     * Additional arguments passed to the linker.
+     */
+    @Input
+    List<String> linkerArgs
+
+    /**
+     * The source object files to be passed to the linker.
+     */
+    @InputFiles @SkipWhenEmpty // Can't use field due to GRADLE-2026
+    FileCollection getSource() {
+        source
+    }
+
+    /**
+     * The library files to be passed to the linker.
+     */
+    @InputFiles
+    FileCollection libs
+
+    /**
+     * Adds a set of object files to be linked.
+     * The provided source object is evaluated as per {@link org.gradle.api.Project#files(Object...)}.
+     */
+    void source(Object source) {
+        this.source.from source
+    }
+
+    /**
+     * Adds a set of library files to be linked.
+     * The provided libs object is evaluated as per {@link org.gradle.api.Project#files(Object...)}.
+     */
+    void lib(Object libs) {
+        this.libs.from libs
+    }
+
+    @TaskAction
+    void link() {
+        def cleaner = new SimpleStaleClassCleaner(getOutputs())
+        cleaner.setDestinationDir(getDestinationDir())
+        cleaner.execute()
+
+        def spec = createLinkerSpec()
+        spec.tempDir = getTemporaryDir()
+
+        spec.outputFile = getOutputFile()
+        spec.source = getSource()
+        spec.libs = getLibs()
+        spec.args = getLinkerArgs()
+
+        def result = toolChain.createLinker().execute(spec)
+        didWork = result.didWork
+    }
+
+    protected abstract LinkerSpec createLinkerSpec();
+}
diff --git a/subprojects/cpp/src/main/groovy/org/gradle/nativecode/base/tasks/BuildBinaryTask.java b/subprojects/cpp/src/main/groovy/org/gradle/nativecode/base/tasks/BuildBinaryTask.java
new file mode 100644
index 0000000..2ce0e3f
--- /dev/null
+++ b/subprojects/cpp/src/main/groovy/org/gradle/nativecode/base/tasks/BuildBinaryTask.java
@@ -0,0 +1,28 @@
+/*
+ * Copyright 2013 the original author or authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.gradle.nativecode.base.tasks;
+
+/**
+ * A task that combines a set of object files into a single binary.
+ */
+public interface BuildBinaryTask {
+    /**
+     * Adds a set of object files to be combined into the file binary.
+     * The provided source object is evaluated as per {@link org.gradle.api.Project#files(Object...)}.
+     */
+    void source(Object source);
+}
diff --git a/subprojects/cpp/src/main/groovy/org/gradle/nativecode/base/tasks/CreateStaticLibrary.groovy b/subprojects/cpp/src/main/groovy/org/gradle/nativecode/base/tasks/CreateStaticLibrary.groovy
new file mode 100644
index 0000000..ffea788
--- /dev/null
+++ b/subprojects/cpp/src/main/groovy/org/gradle/nativecode/base/tasks/CreateStaticLibrary.groovy
@@ -0,0 +1,103 @@
+/*
+ * Copyright 2013 the original author or authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.gradle.nativecode.base.tasks
+
+import org.gradle.api.DefaultTask
+import org.gradle.api.Incubating
+import org.gradle.api.file.FileCollection
+import org.gradle.api.tasks.Input
+import org.gradle.api.tasks.InputFiles
+import org.gradle.api.tasks.OutputFile
+import org.gradle.api.tasks.SkipWhenEmpty
+import org.gradle.api.tasks.TaskAction
+import org.gradle.nativecode.base.ToolChain
+import org.gradle.nativecode.base.internal.StaticLibraryArchiverSpec
+
+import javax.inject.Inject
+
+/**
+ * Assembles a static library from object files.
+ */
+ at Incubating
+class CreateStaticLibrary extends DefaultTask implements BuildBinaryTask {
+    private FileCollection source
+
+    @Inject
+    CreateStaticLibrary() {
+        source = project.files()
+    }
+
+    /**
+     * The tool chain used for creating the static library.
+     */
+    ToolChain toolChain
+
+    // Invalidate output when the tool chain output changes
+    @Input
+    def getOutputType() {
+        return toolChain.outputType
+    }
+
+    /**
+     * The file where the output binary will be located.
+     */
+    @OutputFile
+    File outputFile
+
+    /**
+     * The source object files to be passed to the archiver.
+     */
+    @InputFiles @SkipWhenEmpty // Can't use field due to GRADLE-2026
+    FileCollection getSource() {
+        source
+    }
+
+    /**
+     * Adds a set of object files to be linked.
+     * <p>
+     * The provided source object is evaluated as per {@link org.gradle.api.Project#files(Object...)}.
+     */
+    void source(Object source) {
+        this.source.from source
+    }
+
+    /**
+     * Additional arguments passed to the archiver.
+     */
+    @Input
+    List<String> staticLibArgs
+
+    @TaskAction
+    void link() {
+        def spec = new Spec()
+        spec.tempDir = getTemporaryDir()
+
+        spec.outputFile = getOutputFile()
+        spec.source = getSource()
+        spec.args = getStaticLibArgs()
+
+        def result = toolChain.createStaticLibraryArchiver().execute(spec)
+        didWork = result.didWork
+    }
+
+    private static class Spec implements StaticLibraryArchiverSpec {
+        Iterable<File> source;
+        File outputFile;
+        File tempDir;
+        Iterable<String> args = new ArrayList<String>();
+    }
+}
diff --git a/subprojects/cpp/src/main/groovy/org/gradle/nativecode/base/tasks/InstallExecutable.groovy b/subprojects/cpp/src/main/groovy/org/gradle/nativecode/base/tasks/InstallExecutable.groovy
new file mode 100644
index 0000000..3d25648
--- /dev/null
+++ b/subprojects/cpp/src/main/groovy/org/gradle/nativecode/base/tasks/InstallExecutable.groovy
@@ -0,0 +1,150 @@
+/*
+ * Copyright 2013 the original author or authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.gradle.nativecode.base.tasks
+import org.gradle.api.Action
+import org.gradle.api.DefaultTask
+import org.gradle.api.Incubating
+import org.gradle.api.file.CopySpec
+import org.gradle.api.file.FileCollection
+import org.gradle.api.internal.file.FileOperations
+import org.gradle.api.tasks.InputFile
+import org.gradle.api.tasks.InputFiles
+import org.gradle.api.tasks.OutputDirectory
+import org.gradle.api.tasks.TaskAction
+import org.gradle.internal.nativeplatform.filesystem.FileSystem
+import org.gradle.internal.os.OperatingSystem
+import org.gradle.internal.reflect.Instantiator
+import org.gradle.nativecode.base.ToolChain
+import org.gradle.nativecode.toolchain.Gcc
+
+import javax.inject.Inject
+/**
+ * Installs an executable with it's dependent libraries so it can be easily executed.
+ */
+ at Incubating
+public class InstallExecutable extends DefaultTask {
+
+    private final Instantiator instantiator
+    private final FileOperations fileOperations
+
+    @Inject
+    InstallExecutable(Instantiator instantiator, FileOperations fileOperations) {
+        this.instantiator = instantiator
+        this.fileOperations = fileOperations
+        this.libs = project.files()
+    }
+
+    /**
+     * The tool chain used for linking.
+     */
+    ToolChain toolChain
+
+    /**
+     * The directory to install files into.
+     */
+    @OutputDirectory
+    File destinationDir
+
+    /**
+     * The executable file to install.
+     */
+    @InputFile
+    File executable
+
+    /**
+     * The library files that should be installed.
+     */
+    @InputFiles
+    FileCollection libs
+
+    /**
+     * Adds a set of library files to be installed.
+     * The provided libs object is evaluated as per {@link org.gradle.api.Project#files(Object...)}.
+     */
+    void lib(Object libs) {
+        this.libs.from libs
+    }
+
+    // TODO:DAZ Once we introduce a public type for OperatingSystem, make it configurable here
+    private OperatingSystem os = OperatingSystem.current()
+
+    @TaskAction
+    void install() {
+        if (os.windows) {
+            installWindows()
+        } else {
+            installUnix()
+        }
+    }
+
+    private void installWindows() {
+        final destination = getDestinationDir()
+        final File executable = getExecutable()
+
+        installToDir(new File(destination, "lib"))
+
+        StringBuilder toolChainPath = new StringBuilder()
+        if (toolChain in Gcc) {
+            // Gcc on windows requires the path to be set
+            toolChainPath.append("SET PATH=")
+            for (File path : ((Gcc) toolChain).paths) {
+                toolChainPath.append(path.absolutePath).append(";")
+            }
+            toolChainPath.append("%PATH%")
+        }
+
+        File script = new File(destination, os.getScriptName(executable.name));
+        script.text = """
+ at echo off
+SETLOCAL
+$toolChainPath
+CALL "%~dp0lib\\${executable.name}"
+
+ENDLOCAL
+"""
+    }
+
+    private void installUnix() {
+        final destination = getDestinationDir()
+        final executable = getExecutable()
+
+        installToDir(new File(destination, "lib"))
+
+        File script = new File(destination, executable.name);
+        script.text = """
+#/bin/sh
+APP_BASE_NAME=`dirname "\$0"`
+export DYLD_LIBRARY_PATH="\$APP_BASE_NAME/lib"
+export LD_LIBRARY_PATH="\$APP_BASE_NAME/lib"
+exec "\$APP_BASE_NAME/lib/${executable.name}" \"\$@\"
+"""
+
+        FileSystem fileSystem = getServices().get(FileSystem.class);
+        fileSystem.chmod(script, 0755)
+    }
+
+    private void installToDir(File binaryDir) {
+        fileOperations.sync(new Action<CopySpec>() {
+            void execute(CopySpec copySpec) {
+                copySpec.into(binaryDir)
+                copySpec.from(getExecutable())
+                copySpec.from(getLibs())
+            }
+        })
+    }
+
+}
diff --git a/subprojects/cpp/src/main/groovy/org/gradle/nativecode/base/tasks/LinkExecutable.groovy b/subprojects/cpp/src/main/groovy/org/gradle/nativecode/base/tasks/LinkExecutable.groovy
new file mode 100644
index 0000000..710cbbf
--- /dev/null
+++ b/subprojects/cpp/src/main/groovy/org/gradle/nativecode/base/tasks/LinkExecutable.groovy
@@ -0,0 +1,40 @@
+/*
+ * Copyright 2013 the original author or authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.gradle.nativecode.base.tasks
+import org.gradle.api.Incubating
+import org.gradle.nativecode.base.internal.ExecutableLinkerSpec
+import org.gradle.nativecode.base.internal.LinkerSpec
+
+/**
+ * Links a binary executable from object files and libraries.
+ */
+ at Incubating
+class LinkExecutable extends AbstractLinkTask {
+
+    @Override
+    protected LinkerSpec createLinkerSpec() {
+        return new Spec()
+    }
+
+    private static class Spec implements ExecutableLinkerSpec {
+        Iterable<File> libs;
+        Iterable<File> source;
+        File outputFile;
+        File tempDir;
+        Iterable<String> args = new ArrayList<String>();
+    }
+}
diff --git a/subprojects/cpp/src/main/groovy/org/gradle/nativecode/base/tasks/LinkSharedLibrary.groovy b/subprojects/cpp/src/main/groovy/org/gradle/nativecode/base/tasks/LinkSharedLibrary.groovy
new file mode 100644
index 0000000..b850d5a
--- /dev/null
+++ b/subprojects/cpp/src/main/groovy/org/gradle/nativecode/base/tasks/LinkSharedLibrary.groovy
@@ -0,0 +1,44 @@
+/*
+ * Copyright 2013 the original author or authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.gradle.nativecode.base.tasks
+import org.gradle.api.Incubating
+import org.gradle.nativecode.base.internal.LinkerSpec
+import org.gradle.nativecode.base.internal.SharedLibraryLinkerSpec
+
+/**
+ * Links a binary shared library from object files and imported libraries.
+ */
+ at Incubating
+class LinkSharedLibrary extends AbstractLinkTask {
+    @Override
+    protected LinkerSpec createLinkerSpec() {
+        return new Spec()
+    }
+
+    private static class Spec implements SharedLibraryLinkerSpec {
+        Iterable<File> libs;
+        Iterable<File> source;
+        File outputFile;
+        File tempDir;
+        Iterable<String> args = new ArrayList<String>();
+        String installName;
+
+        public String getInstallName() {
+            return installName == null ? getOutputFile().getName() : installName;
+        }
+    }
+}
diff --git a/subprojects/cpp/src/main/groovy/org/gradle/nativecode/base/tasks/package-info.java b/subprojects/cpp/src/main/groovy/org/gradle/nativecode/base/tasks/package-info.java
new file mode 100644
index 0000000..1aeb2d3
--- /dev/null
+++ b/subprojects/cpp/src/main/groovy/org/gradle/nativecode/base/tasks/package-info.java
@@ -0,0 +1,20 @@
+/*
+ * Copyright 2013 the original author or authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+/**
+ * Tasks for building native component projects.
+ */
+package org.gradle.nativecode.base.tasks;
diff --git a/subprojects/cpp/src/main/groovy/org/gradle/nativecode/cdt/CdtIdePlugin.groovy b/subprojects/cpp/src/main/groovy/org/gradle/nativecode/cdt/CdtIdePlugin.groovy
new file mode 100644
index 0000000..3601699
--- /dev/null
+++ b/subprojects/cpp/src/main/groovy/org/gradle/nativecode/cdt/CdtIdePlugin.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.nativecode.cdt
+
+import org.gradle.api.Incubating
+import org.gradle.api.Project
+import org.gradle.api.Plugin
+import org.gradle.api.InvalidUserDataException
+import org.gradle.api.tasks.Delete
+
+import org.gradle.nativecode.cdt.model.ProjectSettings
+import org.gradle.nativecode.cdt.model.ProjectDescriptor
+import org.gradle.nativecode.cdt.model.CprojectSettings
+import org.gradle.nativecode.cdt.model.CprojectDescriptor
+
+import org.gradle.nativecode.cdt.tasks.GenerateMetadataFileTask
+
+ at Incubating
+class CdtIdePlugin implements Plugin<Project> {
+
+    void apply(Project project) {
+        project.apply(plugin: "binaries")
+        def metadataFileTasks = [addCreateProjectDescriptor(project), addCreateCprojectDescriptor(project)]
+
+        project.task("cleanCdt", type: Delete) {
+            delete metadataFileTasks*.outputs*.files
+        }
+
+        project.task("cdt", dependsOn: metadataFileTasks)
+    }
+
+    private addCreateProjectDescriptor(Project project) {
+        project.task("cdtProject", type: GenerateMetadataFileTask) {
+            inputFile = project.file(".project")
+            outputFile = project.file(".project")
+            factory { new ProjectDescriptor() }
+            onConfigure { new ProjectSettings(name: project.name).applyTo(it) }
+        }
+    }
+
+    private addCreateCprojectDescriptor(Project project) {
+        project.task("cdtCproject", type: GenerateMetadataFileTask) { task ->
+            
+            [project.executables, project.libraries]*.all { binary ->
+                if (binary.name == "main") {
+                    task.settings = new CprojectSettings(binary, project)
+                }
+            }
+            
+            doFirst {
+                if (task.settings == null) {
+                    throw new InvalidUserDataException("There is neither a main binary or library")
+                }
+            }
+            
+            inputs.files { task.settings.includeRoots }
+            inputFile = project.file(".cproject")
+            outputFile = project.file(".cproject")
+            factory { new CprojectDescriptor() }
+            onConfigure { descriptor ->
+                task.settings.applyTo(descriptor)
+            }
+        }
+    }
+
+}
\ No newline at end of file
diff --git a/subprojects/cpp/src/main/groovy/org/gradle/nativecode/cdt/model/CprojectDescriptor.groovy b/subprojects/cpp/src/main/groovy/org/gradle/nativecode/cdt/model/CprojectDescriptor.groovy
new file mode 100644
index 0000000..a0c01bf
--- /dev/null
+++ b/subprojects/cpp/src/main/groovy/org/gradle/nativecode/cdt/model/CprojectDescriptor.groovy
@@ -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.nativecode.cdt.model
+
+import org.gradle.api.Incubating
+import org.gradle.api.internal.xml.XmlTransformer
+import org.gradle.plugins.ide.internal.generator.XmlPersistableConfigurationObject
+
+/**
+ * The actual .cproject descriptor file.
+ */
+ at Incubating
+class CprojectDescriptor extends XmlPersistableConfigurationObject {
+
+    private static final boolean LINUX_NOT_MACOS = true
+    
+    static public final String GNU_COMPILER_TOOL_ID_PREFIX = "cdt.managedbuild.tool.gnu.cpp.compiler"
+    static public final String GNU_COMPILER_TOOL_INCLUDE_PATHS_OPTION_PREFIX = "gnu.cpp.compiler.option.include.paths"
+
+    // linux
+    static public final String GNU_LINKER_TOOL_ID_PREFIX = LINUX_NOT_MACOS ? "cdt.managedbuild.tool.gnu.cpp.linker" : "cdt.managedbuild.tool.macosx.cpp.linker.macosx"
+    static public final String GNU_LINKER_TOOL_LIBS_PATHS_OPTION_PREFIX = LINUX_NOT_MACOS ? "gnu.cpp.link.option.userobjs" : "macosx.cpp.link.option.userobjs"
+
+    CprojectDescriptor() {
+        super(new XmlTransformer())
+    }
+
+    protected String getDefaultResourceName() {
+        LINUX_NOT_MACOS ? 'defaultCproject-linux.xml' : 'defaultCproject-macos.xml'
+    }
+
+    NodeList getConfigurations() {
+        new NodeList(xml.storageModule.cconfiguration.storageModule.findAll { it. at moduleId == "cdtBuildSystem" }.collect { it.configuration[0] })
+    }
+
+    NodeList getRootToolChains() {
+        new NodeList(configurations.folderInfo.findAll { it. at resourcePath == "" }).toolChain
+    }
+
+    NodeList getRootCppCompilerTools() {
+        new NodeList(rootToolChains.tool.findAll { isGnuCompilerTool(it) })
+    }
+
+    NodeList getRootCppLinkerTools() {
+        new NodeList(rootToolChains.tool.findAll { isGnuLinkerTool(it) })
+    }
+
+    boolean isGnuCompilerTool(Node node) {
+        node.name() == "tool" && node. at id.startsWith(GNU_COMPILER_TOOL_ID_PREFIX)
+    }
+
+    boolean isGnuLinkerTool(Node node) {
+        node.name() == "tool" && node. at id.startsWith(GNU_LINKER_TOOL_ID_PREFIX)
+    }
+
+    Node getOrCreateIncludePathsOption(compilerToolNode) {
+        if (!isGnuCompilerTool(compilerToolNode)) {
+            throw new IllegalArgumentException("Arg must be a gnu compiler tool def, was $compilerToolNode")
+        }
+
+        def includePathsOption = compilerToolNode.option.find { it. at id.startsWith(GNU_COMPILER_TOOL_INCLUDE_PATHS_OPTION_PREFIX) }
+        if (!includePathsOption) {
+            includePathsOption = compilerToolNode.appendNode(
+                "option", [
+                    id: createId(GNU_COMPILER_TOOL_INCLUDE_PATHS_OPTION_PREFIX),
+                    superClass: GNU_COMPILER_TOOL_INCLUDE_PATHS_OPTION_PREFIX,
+                    valueType: "includePath"
+                ]
+            )
+        }
+
+        includePathsOption
+    }
+
+    Node getOrCreateLibsOption(linkerToolNode) {
+        if (!isGnuLinkerTool(linkerToolNode)) {
+            throw new IllegalArgumentException("Arg must be a gnu linker tool def, was $linkerToolNode")
+        }
+
+        def libsOption = linkerToolNode.option.find { it. at id.startsWith(GNU_LINKER_TOOL_LIBS_PATHS_OPTION_PREFIX) }
+        if (!libsOption) {
+            libsOption = linkerToolNode.appendNode(
+                "option", [
+                    id: createId(GNU_LINKER_TOOL_LIBS_PATHS_OPTION_PREFIX),
+                    superClass: GNU_LINKER_TOOL_LIBS_PATHS_OPTION_PREFIX,
+                    valueType: "userObjs"
+                ]
+            )
+        }
+
+        libsOption
+    }
+
+    String createId(String prefix) {
+        prefix + "." + new java.text.SimpleDateFormat("yyMMddHHmmssS").format(new Date())
+    }
+
+    protected void store(Node xml) {
+        transformAction {
+            StringBuilder xmlString = it.asString()
+            xmlString.insert(xmlString.indexOf("\n") + 1, "<?fileVersion 4.0.0?>\n")
+        }
+    }
+}
\ No newline at end of file
diff --git a/subprojects/cpp/src/main/groovy/org/gradle/nativecode/cdt/model/CprojectSettings.groovy b/subprojects/cpp/src/main/groovy/org/gradle/nativecode/cdt/model/CprojectSettings.groovy
new file mode 100644
index 0000000..132c2d2
--- /dev/null
+++ b/subprojects/cpp/src/main/groovy/org/gradle/nativecode/cdt/model/CprojectSettings.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.nativecode.cdt.model
+
+import org.gradle.api.Incubating
+import org.gradle.api.file.ConfigurableFileCollection
+import org.gradle.api.internal.project.ProjectInternal
+import org.gradle.nativecode.base.Executable
+import org.gradle.nativecode.base.HeaderExportingSourceSet
+import org.gradle.nativecode.base.Library
+import org.gradle.nativecode.base.NativeComponent
+import org.gradle.nativecode.base.NativeDependencySet
+import org.gradle.nativecode.language.cpp.CppSourceSet
+
+/**
+ * Exposes a more logical view of the actual .cproject descriptor file
+ */
+ at Incubating
+// TODO:DAZ I'm sure this is now broken
+class CprojectSettings {
+
+    NativeComponent binary
+    private final ConfigurableFileCollection includeRoots
+    private final ConfigurableFileCollection libs
+
+    CprojectSettings(NativeComponent binary, ProjectInternal project) {
+        this.binary = binary
+        includeRoots = project.files()
+        libs = project.files()
+
+        binary.source.withType(HeaderExportingSourceSet).all {
+            includeRoots.builtBy(it.exportedHeaders) // have to manually add because we use srcDirs in from, not the real collection
+            includeRoots.from(it.exportedHeaders.srcDirs)
+        }
+
+        binary.source.withType(CppSourceSet).all { sourceSet ->
+            sourceSet.libs.each { NativeDependencySet lib ->
+                this.libs.from(lib.linkFiles)
+                this.includeRoots.from(lib.includeRoots)
+            }
+        }
+    }
+
+    void applyTo(CprojectDescriptor descriptor) {
+        if (binary) {
+            applyBinaryTo(descriptor)
+        } else {
+            throw new IllegalStateException("no binary set")
+        }
+    }
+
+    private applyBinaryTo(CprojectDescriptor descriptor) {
+        descriptor.rootCppCompilerTools.each { compiler ->
+            def includePathsOption = descriptor.getOrCreateIncludePathsOption(compiler)
+            new LinkedList(includePathsOption.children()).each { includePathsOption.remove(it) }
+            includeRoots.each { includeRoot ->
+                includePathsOption.appendNode("listOptionValue", [builtIn: "false", value: includeRoot.absolutePath])
+            }
+        }
+
+        descriptor.rootCppLinkerTools.each { linker ->
+            def libsOption = descriptor.getOrCreateLibsOption(linker)
+            new LinkedList(libsOption.children()).each { libsOption.remove(it) }
+            libs.each { lib ->
+                libsOption.appendNode("listOptionValue", [builtIn: "false", value: lib.absolutePath])
+            }
+        }
+
+        def extension = ""
+        def type 
+        if (binary instanceof Library) {
+            type = "org.eclipse.cdt.build.core.buildArtefactType.sharedLib"
+        } else if (binary instanceof Executable) {
+            type = "org.eclipse.cdt.build.core.buildArtefactType.exe"
+        } else {
+            throw new IllegalStateException("The binary $binary is of a type that we don't know about")
+        }
+        
+        descriptor.configurations.each { conf ->
+            conf. at buildArtefactType = type
+            conf. at artifactExtension = extension
+            def buildPropsPairs = conf. at buildProperties.split(",")
+            def buildProps = [:]
+            buildPropsPairs.each {
+                def parts = it.split("=", 2)
+                buildProps[parts[0]] = parts[1]
+            }
+            buildProps["org.eclipse.cdt.build.core.buildArtefactType"] = type
+            buildPropsPairs = buildProps.collect { k, v -> "$k=$v"}
+            conf. at buildProperties = buildPropsPairs.join(",")
+        }
+    }
+}
\ No newline at end of file
diff --git a/subprojects/cpp/src/main/groovy/org/gradle/nativecode/cdt/model/ProjectDescriptor.groovy b/subprojects/cpp/src/main/groovy/org/gradle/nativecode/cdt/model/ProjectDescriptor.groovy
new file mode 100644
index 0000000..6df3b54
--- /dev/null
+++ b/subprojects/cpp/src/main/groovy/org/gradle/nativecode/cdt/model/ProjectDescriptor.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.nativecode.cdt.model
+
+import org.gradle.api.Incubating
+import org.gradle.api.internal.xml.XmlTransformer
+import org.gradle.plugins.ide.internal.generator.XmlPersistableConfigurationObject
+
+/**
+ * The actual .project descriptor file.
+ */
+ at Incubating
+class ProjectDescriptor extends XmlPersistableConfigurationObject {
+
+    ProjectDescriptor() {
+        super(new XmlTransformer())
+    }
+
+    protected String getDefaultResourceName() {
+        'defaultProject.xml'
+    }
+
+    Node getOrCreate(String name) {
+        getOrCreate(xml, name)
+    }
+
+    Node findBuildCommand(Closure predicate) {
+        xml.buildSpec[0].buildCommand.find(predicate)
+    }
+
+    Node getOrCreate(Node parent, String name) {
+        def node = parent.get(name)
+        node ? node.first() : parent.appendNode(name)
+    }
+}
\ No newline at end of file
diff --git a/subprojects/cpp/src/main/groovy/org/gradle/nativecode/cdt/model/ProjectSettings.groovy b/subprojects/cpp/src/main/groovy/org/gradle/nativecode/cdt/model/ProjectSettings.groovy
new file mode 100644
index 0000000..8dd00b4
--- /dev/null
+++ b/subprojects/cpp/src/main/groovy/org/gradle/nativecode/cdt/model/ProjectSettings.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.nativecode.cdt.model
+
+import org.gradle.api.Incubating
+
+/**
+ * Gradle model element, the configurable parts of the .project file.
+ */
+ at Incubating
+class ProjectSettings {
+    String name
+
+    /**
+     * Apply this logical model to the physical descriptor
+     */
+    void applyTo(ProjectDescriptor descriptor) {
+        descriptor.getOrCreate("name").value = name
+
+        // not anywhere close to right at all, very hardcoded.
+        def genMakeBuilderBuildCommand = descriptor.findBuildCommand { it.name[0].text() == "org.eclipse.cdt.managedbuilder.core.genmakebuilder" }
+        if (genMakeBuilderBuildCommand) {
+            def dict = genMakeBuilderBuildCommand.arguments[0].dictionary.find { it.key[0].text() == "org.eclipse.cdt.make.core.buildLocation" }
+            if (dict) {
+                dict.value[0].value = "\${workspace_loc:/$name/Debug}"
+            }
+        }
+
+    }
+}
\ No newline at end of file
diff --git a/subprojects/cpp/src/main/groovy/org/gradle/nativecode/cdt/tasks/GenerateMetadataFileTask.groovy b/subprojects/cpp/src/main/groovy/org/gradle/nativecode/cdt/tasks/GenerateMetadataFileTask.groovy
new file mode 100644
index 0000000..62889a4
--- /dev/null
+++ b/subprojects/cpp/src/main/groovy/org/gradle/nativecode/cdt/tasks/GenerateMetadataFileTask.groovy
@@ -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.nativecode.cdt.tasks
+
+import org.gradle.api.Incubating
+import org.gradle.api.internal.ClosureBackedAction
+import org.gradle.internal.Factory
+import org.gradle.listener.ActionBroadcast
+import org.gradle.nativecode.cdt.model.CprojectSettings
+import org.gradle.plugins.ide.api.GeneratorTask
+import org.gradle.plugins.ide.internal.generator.generator.PersistableConfigurationObject
+import org.gradle.plugins.ide.internal.generator.generator.PersistableConfigurationObjectGenerator
+
+ at Incubating
+class GenerateMetadataFileTask<T extends PersistableConfigurationObject> extends GeneratorTask<T> {
+
+    Factory<T> factory
+    ActionBroadcast<T> configures = new ActionBroadcast<T>()
+    CprojectSettings settings
+
+    GenerateMetadataFileTask() {
+        generator = new PersistableConfigurationObjectGenerator() {
+            public create() {
+                GenerateMetadataFileTask.this.factory.create();
+            }
+
+            public void configure(object) {
+                GenerateMetadataFileTask.this.configures.execute(object);
+            }
+        }
+    }
+
+    void factory(Closure factory) {
+        this.factory = factory as Factory
+    }
+
+    void onConfigure(Closure configure) {
+        configures.add(new ClosureBackedAction(configure))
+    }
+}
\ No newline at end of file
diff --git a/subprojects/cpp/src/main/groovy/org/gradle/nativecode/language/asm/AssemblerSourceSet.java b/subprojects/cpp/src/main/groovy/org/gradle/nativecode/language/asm/AssemblerSourceSet.java
new file mode 100644
index 0000000..7d3a769
--- /dev/null
+++ b/subprojects/cpp/src/main/groovy/org/gradle/nativecode/language/asm/AssemblerSourceSet.java
@@ -0,0 +1,24 @@
+/*
+ * Copyright 2013 the original author or authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.gradle.nativecode.language.asm;
+
+import org.gradle.language.base.LanguageSourceSet;
+
+/**
+ * A set of assembly language sources.
+ */
+public interface AssemblerSourceSet extends LanguageSourceSet {
+}
diff --git a/subprojects/cpp/src/main/groovy/org/gradle/nativecode/language/asm/internal/AssembleSpec.java b/subprojects/cpp/src/main/groovy/org/gradle/nativecode/language/asm/internal/AssembleSpec.java
new file mode 100644
index 0000000..adca9c9
--- /dev/null
+++ b/subprojects/cpp/src/main/groovy/org/gradle/nativecode/language/asm/internal/AssembleSpec.java
@@ -0,0 +1,38 @@
+/*
+ * Copyright 2012 the original author or authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.gradle.nativecode.language.asm.internal;
+
+import org.gradle.nativecode.base.internal.BinaryToolSpec;
+
+import java.io.File;
+
+/**
+ * A compile spec that will be used to generate object files for combining into a native binary.
+ */
+public interface AssembleSpec extends BinaryToolSpec {
+    File getObjectFileDir();
+
+    void setObjectFileDir(File objectFileDir);
+
+    Iterable<File> getSource();
+
+    void setSource(Iterable<File> source);
+
+    Iterable<String> getArgs();
+
+    void setArgs(Iterable<String> args);
+}
diff --git a/subprojects/cpp/src/main/groovy/org/gradle/nativecode/language/asm/internal/DefaultAssembleSpec.java b/subprojects/cpp/src/main/groovy/org/gradle/nativecode/language/asm/internal/DefaultAssembleSpec.java
new file mode 100644
index 0000000..6f9af42
--- /dev/null
+++ b/subprojects/cpp/src/main/groovy/org/gradle/nativecode/language/asm/internal/DefaultAssembleSpec.java
@@ -0,0 +1,22 @@
+/*
+ * Copyright 2013 the original author or authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.gradle.nativecode.language.asm.internal;
+
+import org.gradle.nativecode.language.base.internal.AbstractBaseCompileSpec;
+
+public class DefaultAssembleSpec extends AbstractBaseCompileSpec implements AssembleSpec {
+}
diff --git a/subprojects/cpp/src/main/groovy/org/gradle/nativecode/language/asm/internal/DefaultAssemblerSourceSet.java b/subprojects/cpp/src/main/groovy/org/gradle/nativecode/language/asm/internal/DefaultAssemblerSourceSet.java
new file mode 100644
index 0000000..1bccc94
--- /dev/null
+++ b/subprojects/cpp/src/main/groovy/org/gradle/nativecode/language/asm/internal/DefaultAssemblerSourceSet.java
@@ -0,0 +1,28 @@
+/*
+ * Copyright 2013 the original author or authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.gradle.nativecode.language.asm.internal;
+
+import org.gradle.api.internal.project.ProjectInternal;
+import org.gradle.language.base.FunctionalSourceSet;
+import org.gradle.nativecode.language.asm.AssemblerSourceSet;
+import org.gradle.nativecode.language.base.internal.AbstractBaseSourceSet;
+
+public class DefaultAssemblerSourceSet extends AbstractBaseSourceSet implements AssemblerSourceSet {
+    public DefaultAssemblerSourceSet(String name, FunctionalSourceSet parent, ProjectInternal project) {
+        super(name, parent, project, "Assembler");
+    }
+}
diff --git a/subprojects/cpp/src/main/groovy/org/gradle/nativecode/language/asm/package-info.java b/subprojects/cpp/src/main/groovy/org/gradle/nativecode/language/asm/package-info.java
new file mode 100644
index 0000000..89e04b4
--- /dev/null
+++ b/subprojects/cpp/src/main/groovy/org/gradle/nativecode/language/asm/package-info.java
@@ -0,0 +1,20 @@
+/*
+ * Copyright 2013 the original author or authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT 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 that model aspects of C++ projects.
+ */
+package org.gradle.nativecode.language.asm;
diff --git a/subprojects/cpp/src/main/groovy/org/gradle/nativecode/language/asm/plugins/AssemblerLangPlugin.groovy b/subprojects/cpp/src/main/groovy/org/gradle/nativecode/language/asm/plugins/AssemblerLangPlugin.groovy
new file mode 100644
index 0000000..07034f1
--- /dev/null
+++ b/subprojects/cpp/src/main/groovy/org/gradle/nativecode/language/asm/plugins/AssemblerLangPlugin.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.nativecode.language.asm.plugins
+
+import org.gradle.api.Incubating
+import org.gradle.api.Plugin
+import org.gradle.api.internal.project.ProjectInternal
+import org.gradle.internal.reflect.Instantiator
+import org.gradle.language.base.plugins.LanguageBasePlugin
+import org.gradle.nativecode.base.NativeBinary
+import org.gradle.nativecode.base.internal.NativeBinaryInternal
+import org.gradle.nativecode.language.asm.AssemblerSourceSet
+import org.gradle.nativecode.language.asm.tasks.Assemble
+
+import javax.inject.Inject
+
+ at Incubating
+class AssemblerLangPlugin implements Plugin<ProjectInternal> {
+    private final Instantiator instantiator;
+
+    @Inject
+    public AssemblerLangPlugin(Instantiator instantiator) {
+        this.instantiator = instantiator;
+    }
+
+    void apply(ProjectInternal project) {
+        project.getPlugins().apply(LanguageBasePlugin.class);
+
+        project.binaries.withType(NativeBinary) { NativeBinaryInternal binary ->
+            binary.source.withType(AssemblerSourceSet).all { AssemblerSourceSet sourceSet ->
+                def compileTask = createAssembleTask(project, binary, sourceSet)
+                binary.builderTask.source compileTask.outputs.files.asFileTree.matching { include '**/*.obj', '**/*.o' }
+            }
+        }
+    }
+
+    private def createAssembleTask(ProjectInternal project, NativeBinaryInternal binary, def sourceSet) {
+        def assembleTask = project.task(binary.namingScheme.getTaskName("assemble", sourceSet.fullName), type: Assemble) {
+            description = "Assembles the $sourceSet sources of $binary"
+        }
+
+        assembleTask.toolChain = binary.toolChain
+
+        assembleTask.source sourceSet.source
+
+        assembleTask.conventionMapping.objectFileDir = { project.file("${project.buildDir}/objectFiles/${binary.namingScheme.outputDirectoryBase}/${sourceSet.fullName}") }
+        assembleTask.conventionMapping.assemblerArgs = { binary.assemblerArgs }
+
+        assembleTask
+    }
+}
\ No newline at end of file
diff --git a/subprojects/cpp/src/main/groovy/org/gradle/nativecode/language/asm/tasks/Assemble.groovy b/subprojects/cpp/src/main/groovy/org/gradle/nativecode/language/asm/tasks/Assemble.groovy
new file mode 100644
index 0000000..32f7734
--- /dev/null
+++ b/subprojects/cpp/src/main/groovy/org/gradle/nativecode/language/asm/tasks/Assemble.groovy
@@ -0,0 +1,89 @@
+/*
+ * Copyright 2013 the original author or authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.gradle.nativecode.language.asm.tasks
+import org.gradle.api.DefaultTask
+import org.gradle.api.Incubating
+import org.gradle.api.file.FileCollection
+import org.gradle.api.tasks.*
+import org.gradle.language.jvm.internal.SimpleStaleClassCleaner
+import org.gradle.nativecode.base.ToolChain
+import org.gradle.nativecode.language.asm.internal.DefaultAssembleSpec
+
+import javax.inject.Inject
+
+/**
+ * Translates Assembly language source files into object files.
+ */
+ at Incubating
+class Assemble extends DefaultTask {
+    private FileCollection source
+
+    ToolChain toolChain
+
+    /**
+     * The directory where object files will be generated.
+     */
+    @OutputDirectory
+    File objectFileDir
+
+    @InputFiles @SkipWhenEmpty // Workaround for GRADLE-2026
+    FileCollection getSource() {
+        source
+    }
+
+    // Invalidate output when the tool chain output changes
+    @Input
+    def getOutputType() {
+        return toolChain.outputType
+    }
+
+    /**
+     * Additional arguments to provide to the assembler.
+     */
+    @Input
+    List<String> assemblerArgs
+
+    @Inject
+    Assemble() {
+        source = project.files()
+    }
+
+    @TaskAction
+    void assemble() {
+        def cleaner = new SimpleStaleClassCleaner(getOutputs())
+        cleaner.setDestinationDir(getObjectFileDir())
+        cleaner.execute()
+
+        def spec = new DefaultAssembleSpec()
+        spec.tempDir = getTemporaryDir()
+
+        spec.objectFileDir = getObjectFileDir()
+        spec.source = getSource()
+        spec.args = getAssemblerArgs()
+
+        def result = toolChain.createAssembler().execute(spec)
+        didWork = result.didWork
+    }
+
+    /**
+     * Adds a set of assembler sources files to be translated.
+     * The provided sourceFiles object is evaluated as per {@link org.gradle.api.Project#files(Object...)}.
+     */
+    void source(Object sourceFiles) {
+        source.from sourceFiles
+    }
+}
diff --git a/subprojects/cpp/src/main/groovy/org/gradle/nativecode/language/base/internal/AbstractBaseCompileSpec.java b/subprojects/cpp/src/main/groovy/org/gradle/nativecode/language/base/internal/AbstractBaseCompileSpec.java
new file mode 100644
index 0000000..4cd560c
--- /dev/null
+++ b/subprojects/cpp/src/main/groovy/org/gradle/nativecode/language/base/internal/AbstractBaseCompileSpec.java
@@ -0,0 +1,78 @@
+/*
+ * Copyright 2013 the original author or authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.gradle.nativecode.language.base.internal;
+
+import java.io.File;
+import java.util.ArrayList;
+
+public abstract class AbstractBaseCompileSpec {
+
+    private Iterable<File> includeRoots;
+    private Iterable<File> source;
+    private Iterable<String> macros = new ArrayList<String>();
+    private Iterable<String> args = new ArrayList<String>();
+    private File objectFileDir;
+    private File tempDir;
+
+    public Iterable<File> getIncludeRoots() {
+        return includeRoots;
+    }
+
+    public void setIncludeRoots(Iterable<File> includeRoots) {
+        this.includeRoots = includeRoots;
+    }
+
+    public Iterable<File> getSource() {
+        return source;
+    }
+
+    public void setSource(Iterable<File> source) {
+        this.source = source;
+    }
+
+    public File getObjectFileDir() {
+        return objectFileDir;
+    }
+
+    public void setObjectFileDir(File objectFileDir) {
+        this.objectFileDir = objectFileDir;
+    }
+
+    public File getTempDir() {
+        return tempDir;
+    }
+
+    public void setTempDir(File tempDir) {
+        this.tempDir = tempDir;
+    }
+
+    public Iterable<String> getMacros() {
+        return macros;
+    }
+
+    public void setMacros(Iterable<String> macros) {
+        this.macros = macros;
+    }
+
+    public void setArgs(Iterable<String> args) {
+        this.args = args;
+    }
+
+    public Iterable<String> getArgs() {
+        return args;
+    }
+}
diff --git a/subprojects/cpp/src/main/groovy/org/gradle/nativecode/language/base/internal/AbstractBaseSourceSet.java b/subprojects/cpp/src/main/groovy/org/gradle/nativecode/language/base/internal/AbstractBaseSourceSet.java
new file mode 100644
index 0000000..892dd5c
--- /dev/null
+++ b/subprojects/cpp/src/main/groovy/org/gradle/nativecode/language/base/internal/AbstractBaseSourceSet.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.nativecode.language.base.internal;
+
+import org.apache.commons.lang.StringUtils;
+import org.gradle.api.Action;
+import org.gradle.api.file.SourceDirectorySet;
+import org.gradle.api.internal.file.DefaultSourceDirectorySet;
+import org.gradle.api.internal.project.ProjectInternal;
+import org.gradle.api.internal.tasks.DefaultTaskDependency;
+import org.gradle.api.tasks.TaskDependency;
+import org.gradle.language.base.FunctionalSourceSet;
+import org.gradle.language.base.internal.LanguageSourceSetInternal;
+import org.gradle.nativecode.base.internal.ConfigurationBasedNativeDependencySet;
+
+import java.util.ArrayList;
+import java.util.Collection;
+import java.util.List;
+import java.util.Map;
+
+/**
+ * A convenience base class for implementing language source sets.
+ * Note that this class 'implements' all required methods for {@link org.gradle.nativecode.base.HeaderExportingSourceSet}
+ * and {@link org.gradle.nativecode.base.DependentSourceSet} but does not add those types to the API.
+ */
+public abstract class AbstractBaseSourceSet implements LanguageSourceSetInternal {
+
+    private final String name;
+    private final String fullName;
+    private final String typeName;
+
+    private final DefaultSourceDirectorySet exportedHeaders;
+    private final DefaultSourceDirectorySet source;
+    private final List<Object> libs = new ArrayList<Object>();
+    private final ConfigurationBasedNativeDependencySet configurationDependencySet;
+
+    public AbstractBaseSourceSet(String name, FunctionalSourceSet parent, ProjectInternal project, String typeName) {
+        this.name = name;
+        this.fullName = parent.getName() + StringUtils.capitalize(name);
+        this.typeName = typeName;
+
+        this.exportedHeaders = new DefaultSourceDirectorySet("exported headers", project.getFileResolver());
+        this.source = new DefaultSourceDirectorySet("source", project.getFileResolver());
+        this.configurationDependencySet = new ConfigurationBasedNativeDependencySet(project, fullName);
+        
+        libs.add(configurationDependencySet);
+    }
+
+    public String getName() {
+        return name;
+    }
+
+    public String getFullName() {
+        return fullName;
+    }
+
+    @Override
+    public String toString() {
+        return String.format("%s source '%s'", typeName, getFullName());
+    }
+
+    public TaskDependency getBuildDependencies() {
+        // TODO -
+        return new DefaultTaskDependency();
+    }
+
+    public SourceDirectorySet getExportedHeaders() {
+        return exportedHeaders;
+    }
+
+    public void exportedHeaders(Action<SourceDirectorySet> config) {
+        config.execute(getExportedHeaders());
+    }
+
+    public SourceDirectorySet getSource() {
+        return source;
+    }
+
+    public void source(Action<SourceDirectorySet> config) {
+        config.execute(getSource());
+    }
+
+    public Collection<?> getLibs() {
+        return libs;
+    }
+
+    public void lib(Object library) {
+        libs.add(library);
+    }
+
+    public void dependency(Map<?, ?> dep) {
+        configurationDependencySet.add(dep);
+    }
+}
\ No newline at end of file
diff --git a/subprojects/cpp/src/main/groovy/org/gradle/nativecode/language/base/internal/NativeCompileSpec.java b/subprojects/cpp/src/main/groovy/org/gradle/nativecode/language/base/internal/NativeCompileSpec.java
new file mode 100644
index 0000000..38a8bc3
--- /dev/null
+++ b/subprojects/cpp/src/main/groovy/org/gradle/nativecode/language/base/internal/NativeCompileSpec.java
@@ -0,0 +1,50 @@
+/*
+ * Copyright 2012 the original author or authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.gradle.nativecode.language.base.internal;
+
+import org.gradle.nativecode.base.internal.BinaryToolSpec;
+
+import java.io.File;
+
+/**
+ * A compile spec that will be used to generate object files for combining into a native binary.
+ */
+public interface NativeCompileSpec extends BinaryToolSpec {
+    File getObjectFileDir();
+
+    void setObjectFileDir(File objectFileDir);
+
+    Iterable<File> getIncludeRoots();
+
+    void setIncludeRoots(Iterable<File> includeRoots);
+
+    Iterable<File> getSource();
+
+    void setSource(Iterable<File> source);
+
+    Iterable<String> getMacros();
+
+    void setMacros(Iterable<String> macros);
+
+    Iterable<String> getArgs();
+
+    void setArgs(Iterable<String> args);
+
+    boolean isPositionIndependentCode();
+
+    void setPositionIndependentCode(boolean flag);
+}
diff --git a/subprojects/cpp/src/main/groovy/org/gradle/nativecode/language/base/tasks/AbstractNativeCompileTask.groovy b/subprojects/cpp/src/main/groovy/org/gradle/nativecode/language/base/tasks/AbstractNativeCompileTask.groovy
new file mode 100644
index 0000000..d5a2f84
--- /dev/null
+++ b/subprojects/cpp/src/main/groovy/org/gradle/nativecode/language/base/tasks/AbstractNativeCompileTask.groovy
@@ -0,0 +1,127 @@
+/*
+ * Copyright 2013 the original author or authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.gradle.nativecode.language.base.tasks
+import org.gradle.api.DefaultTask
+import org.gradle.api.Incubating
+import org.gradle.api.file.FileCollection
+import org.gradle.api.file.SourceDirectorySet
+import org.gradle.api.tasks.*
+import org.gradle.language.jvm.internal.SimpleStaleClassCleaner
+import org.gradle.nativecode.base.ToolChain
+import org.gradle.nativecode.base.internal.ToolChainInternal
+import org.gradle.nativecode.language.base.internal.NativeCompileSpec
+
+import javax.inject.Inject
+/**
+ * Compiles native source files into object files.
+ */
+ at Incubating
+abstract class AbstractNativeCompileTask extends DefaultTask {
+    private FileCollection source
+
+    ToolChain toolChain
+
+    /**
+     * Should the compiler generate position independent code?
+     */
+    @Input
+    boolean positionIndependentCode
+
+    /**
+     * The directory where object files will be generated.
+     */
+    @OutputDirectory
+    File objectFileDir
+
+    @InputFiles
+    FileCollection includes
+
+    @InputFiles @SkipWhenEmpty // Workaround for GRADLE-2026
+    FileCollection getSource() {
+        source
+    }
+
+    // Invalidate output when the tool chain output changes
+    @Input
+    def getOutputType() {
+        return toolChain.outputType
+    }
+
+    /**
+     * Macros that should be defined for the compiler.
+     */
+    @Input
+    List<String> macros
+
+    /**
+     * Additional arguments to provide to the compiler.
+     */
+    @Input
+    List<String> compilerArgs
+
+    @Inject
+    AbstractNativeCompileTask() {
+        includes = project.files()
+        source = project.files()
+    }
+
+    @TaskAction
+    void compile() {
+        def cleaner = new SimpleStaleClassCleaner(getOutputs())
+        cleaner.setDestinationDir(getObjectFileDir())
+        cleaner.execute()
+
+        def spec = createCompileSpec()
+        spec.tempDir = getTemporaryDir()
+
+        spec.objectFileDir = getObjectFileDir()
+        spec.includeRoots = getIncludes()
+        spec.source = getSource()
+        spec.macros = getMacros()
+        spec.args = getCompilerArgs()
+        spec.positionIndependentCode = isPositionIndependentCode()
+
+        def result = execute(toolChain as ToolChainInternal, spec)
+        didWork = result.didWork
+    }
+
+    protected abstract NativeCompileSpec createCompileSpec();
+
+    protected abstract WorkResult execute(ToolChainInternal toolChain, NativeCompileSpec spec);
+
+    /**
+     * Add locations where the compiler should search for header files.
+     */
+    void includes(SourceDirectorySet dirs) {
+        includes.from({dirs.srcDirs})
+    }
+
+    /**
+     * Add directories where the compiler should search for header files.
+     */
+    void includes(FileCollection includeRoots) {
+        includes.from(includeRoots)
+    }
+
+    /**
+     * Adds a set of source files to be compiled.
+     * The provided sourceFiles object is evaluated as per {@link org.gradle.api.Project#files(Object...)}.
+     */
+    void source(Object sourceFiles) {
+        source.from sourceFiles
+    }
+}
diff --git a/subprojects/cpp/src/main/groovy/org/gradle/nativecode/language/c/CSourceSet.java b/subprojects/cpp/src/main/groovy/org/gradle/nativecode/language/c/CSourceSet.java
new file mode 100644
index 0000000..147962e
--- /dev/null
+++ b/subprojects/cpp/src/main/groovy/org/gradle/nativecode/language/c/CSourceSet.java
@@ -0,0 +1,28 @@
+/*
+ * Copyright 2013 the original author or authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.gradle.nativecode.language.c;
+
+import org.gradle.api.Incubating;
+import org.gradle.language.base.LanguageSourceSet;
+import org.gradle.nativecode.base.DependentSourceSet;
+import org.gradle.nativecode.base.HeaderExportingSourceSet;
+
+/**
+ * A representation of a unit of C source.
+ */
+ at Incubating
+public interface CSourceSet extends HeaderExportingSourceSet, LanguageSourceSet, DependentSourceSet {
+}
\ No newline at end of file
diff --git a/subprojects/cpp/src/main/groovy/org/gradle/nativecode/language/c/internal/CCompileSpec.java b/subprojects/cpp/src/main/groovy/org/gradle/nativecode/language/c/internal/CCompileSpec.java
new file mode 100644
index 0000000..dcf2081
--- /dev/null
+++ b/subprojects/cpp/src/main/groovy/org/gradle/nativecode/language/c/internal/CCompileSpec.java
@@ -0,0 +1,23 @@
+/*
+ * Copyright 2013 the original author or authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.gradle.nativecode.language.c.internal;
+
+import org.gradle.nativecode.language.base.internal.NativeCompileSpec;
+
+public interface CCompileSpec extends NativeCompileSpec {
+
+}
diff --git a/subprojects/cpp/src/main/groovy/org/gradle/nativecode/language/c/internal/DefaultCCompileSpec.java b/subprojects/cpp/src/main/groovy/org/gradle/nativecode/language/c/internal/DefaultCCompileSpec.java
new file mode 100644
index 0000000..4e3cdea
--- /dev/null
+++ b/subprojects/cpp/src/main/groovy/org/gradle/nativecode/language/c/internal/DefaultCCompileSpec.java
@@ -0,0 +1,31 @@
+/*
+ * Copyright 2013 the original author or authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.gradle.nativecode.language.c.internal;
+
+import org.gradle.nativecode.language.base.internal.AbstractBaseCompileSpec;
+
+public class DefaultCCompileSpec extends AbstractBaseCompileSpec implements CCompileSpec {
+    private boolean positionIndependentCode;
+
+    public boolean isPositionIndependentCode() {
+        return positionIndependentCode;
+    }
+
+    public void setPositionIndependentCode(boolean positionIndependentCode) {
+        this.positionIndependentCode = positionIndependentCode;
+    }
+}
diff --git a/subprojects/cpp/src/main/groovy/org/gradle/nativecode/language/c/internal/DefaultCSourceSet.java b/subprojects/cpp/src/main/groovy/org/gradle/nativecode/language/c/internal/DefaultCSourceSet.java
new file mode 100644
index 0000000..ddcf45b
--- /dev/null
+++ b/subprojects/cpp/src/main/groovy/org/gradle/nativecode/language/c/internal/DefaultCSourceSet.java
@@ -0,0 +1,28 @@
+/*
+ * Copyright 2013 the original author or authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.gradle.nativecode.language.c.internal;
+
+import org.gradle.api.internal.project.ProjectInternal;
+import org.gradle.language.base.FunctionalSourceSet;
+import org.gradle.nativecode.language.base.internal.AbstractBaseSourceSet;
+import org.gradle.nativecode.language.c.CSourceSet;
+
+public class DefaultCSourceSet extends AbstractBaseSourceSet implements CSourceSet {
+    public DefaultCSourceSet(String name, FunctionalSourceSet parent, ProjectInternal project) {
+        super(name, parent, project, "C");
+    }
+}
diff --git a/subprojects/cpp/src/main/groovy/org/gradle/nativecode/language/c/package-info.java b/subprojects/cpp/src/main/groovy/org/gradle/nativecode/language/c/package-info.java
new file mode 100644
index 0000000..1d24572
--- /dev/null
+++ b/subprojects/cpp/src/main/groovy/org/gradle/nativecode/language/c/package-info.java
@@ -0,0 +1,20 @@
+/*
+ * Copyright 2013 the original author or authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT 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 that model aspects of C++ projects.
+ */
+package org.gradle.nativecode.language.c;
diff --git a/subprojects/cpp/src/main/groovy/org/gradle/nativecode/language/c/plugins/CLangPlugin.groovy b/subprojects/cpp/src/main/groovy/org/gradle/nativecode/language/c/plugins/CLangPlugin.groovy
new file mode 100644
index 0000000..5141a20
--- /dev/null
+++ b/subprojects/cpp/src/main/groovy/org/gradle/nativecode/language/c/plugins/CLangPlugin.groovy
@@ -0,0 +1,72 @@
+/*
+ * Copyright 2011 the original author or authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.gradle.nativecode.language.c.plugins
+
+import org.gradle.api.Incubating
+import org.gradle.api.Plugin
+import org.gradle.api.internal.project.ProjectInternal
+import org.gradle.internal.reflect.Instantiator
+import org.gradle.language.base.plugins.LanguageBasePlugin
+import org.gradle.nativecode.base.NativeBinary
+import org.gradle.nativecode.base.NativeDependencySet
+import org.gradle.nativecode.base.SharedLibraryBinary
+import org.gradle.nativecode.base.internal.NativeBinaryInternal
+import org.gradle.nativecode.language.c.CSourceSet
+import org.gradle.nativecode.language.c.tasks.CCompile
+
+import javax.inject.Inject
+
+ at Incubating
+class CLangPlugin implements Plugin<ProjectInternal> {
+    private final Instantiator instantiator;
+
+    @Inject
+    public CLangPlugin(Instantiator instantiator) {
+        this.instantiator = instantiator;
+    }
+
+    void apply(ProjectInternal project) {
+        project.getPlugins().apply(LanguageBasePlugin.class);
+
+        project.binaries.withType(NativeBinary) { NativeBinaryInternal binary ->
+            binary.source.withType(CSourceSet).all { CSourceSet sourceSet ->
+                def compileTask = createCompileTask(project, binary, sourceSet)
+                binary.builderTask.source compileTask.outputs.files.asFileTree.matching { include '**/*.obj', '**/*.o' }
+            }
+        }
+    }
+
+    private def createCompileTask(ProjectInternal project, NativeBinaryInternal binary, def sourceSet) {
+        def compileTask = project.task(binary.namingScheme.getTaskName("compile", sourceSet.fullName), type: CCompile) {
+            description = "Compiles the $sourceSet sources of $binary"
+        }
+
+        compileTask.toolChain = binary.toolChain
+        compileTask.positionIndependentCode = binary instanceof SharedLibraryBinary
+
+        compileTask.includes sourceSet.exportedHeaders
+        compileTask.source sourceSet.source
+        binary.libs.each { NativeDependencySet deps ->
+            compileTask.includes deps.includeRoots
+        }
+
+        compileTask.conventionMapping.objectFileDir = { project.file("${project.buildDir}/objectFiles/${binary.namingScheme.outputDirectoryBase}/${sourceSet.fullName}") }
+        compileTask.conventionMapping.macros = { binary.macros }
+        compileTask.conventionMapping.compilerArgs = { binary.compilerArgs }
+
+        compileTask
+    }
+}
\ No newline at end of file
diff --git a/subprojects/cpp/src/main/groovy/org/gradle/nativecode/language/c/tasks/CCompile.groovy b/subprojects/cpp/src/main/groovy/org/gradle/nativecode/language/c/tasks/CCompile.groovy
new file mode 100644
index 0000000..dabd5a2
--- /dev/null
+++ b/subprojects/cpp/src/main/groovy/org/gradle/nativecode/language/c/tasks/CCompile.groovy
@@ -0,0 +1,40 @@
+/*
+ * Copyright 2013 the original author or authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.gradle.nativecode.language.c.tasks
+
+import org.gradle.api.Incubating
+import org.gradle.api.tasks.WorkResult
+import org.gradle.nativecode.base.internal.ToolChainInternal
+import org.gradle.nativecode.language.base.internal.NativeCompileSpec
+import org.gradle.nativecode.language.c.internal.DefaultCCompileSpec
+import org.gradle.nativecode.language.base.tasks.AbstractNativeCompileTask
+
+/**
+ * Compiles C source files into object files.
+ */
+ at Incubating
+class CCompile extends AbstractNativeCompileTask {
+    @Override
+    protected NativeCompileSpec createCompileSpec() {
+        new DefaultCCompileSpec()
+    }
+
+    @Override
+    protected WorkResult execute(ToolChainInternal toolChain, NativeCompileSpec spec) {
+        return toolChain.createCCompiler().execute(spec)
+    }
+}
diff --git a/subprojects/cpp/src/main/groovy/org/gradle/nativecode/language/cpp/CppSourceSet.java b/subprojects/cpp/src/main/groovy/org/gradle/nativecode/language/cpp/CppSourceSet.java
new file mode 100644
index 0000000..dbdc55f
--- /dev/null
+++ b/subprojects/cpp/src/main/groovy/org/gradle/nativecode/language/cpp/CppSourceSet.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.nativecode.language.cpp;
+
+import org.gradle.api.Incubating;
+import org.gradle.language.base.LanguageSourceSet;
+import org.gradle.nativecode.base.DependentSourceSet;
+import org.gradle.nativecode.base.HeaderExportingSourceSet;
+
+/**
+ * A representation of a unit of C++ source.
+ */
+ at Incubating
+public interface CppSourceSet extends HeaderExportingSourceSet, LanguageSourceSet, DependentSourceSet {
+}
\ No newline at end of file
diff --git a/subprojects/cpp/src/main/groovy/org/gradle/nativecode/language/cpp/internal/CppCompileSpec.java b/subprojects/cpp/src/main/groovy/org/gradle/nativecode/language/cpp/internal/CppCompileSpec.java
new file mode 100644
index 0000000..706cf11
--- /dev/null
+++ b/subprojects/cpp/src/main/groovy/org/gradle/nativecode/language/cpp/internal/CppCompileSpec.java
@@ -0,0 +1,23 @@
+/*
+ * Copyright 2012 the original author or authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.gradle.nativecode.language.cpp.internal;
+
+import org.gradle.nativecode.language.base.internal.NativeCompileSpec;
+
+public interface CppCompileSpec extends NativeCompileSpec {
+
+}
diff --git a/subprojects/cpp/src/main/groovy/org/gradle/nativecode/language/cpp/internal/DefaultCppCompileSpec.java b/subprojects/cpp/src/main/groovy/org/gradle/nativecode/language/cpp/internal/DefaultCppCompileSpec.java
new file mode 100644
index 0000000..c50807e
--- /dev/null
+++ b/subprojects/cpp/src/main/groovy/org/gradle/nativecode/language/cpp/internal/DefaultCppCompileSpec.java
@@ -0,0 +1,31 @@
+/*
+ * Copyright 2013 the original author or authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.gradle.nativecode.language.cpp.internal;
+
+import org.gradle.nativecode.language.base.internal.AbstractBaseCompileSpec;
+
+public class DefaultCppCompileSpec extends AbstractBaseCompileSpec implements CppCompileSpec {
+    private boolean positionIndependentCode;
+
+    public boolean isPositionIndependentCode() {
+        return positionIndependentCode;
+    }
+
+    public void setPositionIndependentCode(boolean positionIndependentCode) {
+        this.positionIndependentCode = positionIndependentCode;
+    }
+}
diff --git a/subprojects/cpp/src/main/groovy/org/gradle/nativecode/language/cpp/internal/DefaultCppSourceSet.java b/subprojects/cpp/src/main/groovy/org/gradle/nativecode/language/cpp/internal/DefaultCppSourceSet.java
new file mode 100644
index 0000000..c3e76cc
--- /dev/null
+++ b/subprojects/cpp/src/main/groovy/org/gradle/nativecode/language/cpp/internal/DefaultCppSourceSet.java
@@ -0,0 +1,28 @@
+/*
+ * Copyright 2013 the original author or authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.gradle.nativecode.language.cpp.internal;
+
+import org.gradle.api.internal.project.ProjectInternal;
+import org.gradle.language.base.FunctionalSourceSet;
+import org.gradle.nativecode.language.base.internal.AbstractBaseSourceSet;
+import org.gradle.nativecode.language.cpp.CppSourceSet;
+
+public class DefaultCppSourceSet extends AbstractBaseSourceSet implements CppSourceSet {
+    public DefaultCppSourceSet(String name, FunctionalSourceSet parent, ProjectInternal project) {
+        super(name, parent, project, "C++");
+    }
+}
diff --git a/subprojects/cpp/src/main/groovy/org/gradle/nativecode/language/cpp/package-info.java b/subprojects/cpp/src/main/groovy/org/gradle/nativecode/language/cpp/package-info.java
new file mode 100644
index 0000000..4a87e2c
--- /dev/null
+++ b/subprojects/cpp/src/main/groovy/org/gradle/nativecode/language/cpp/package-info.java
@@ -0,0 +1,20 @@
+/*
+ * Copyright 2013 the original author or authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT 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 that model aspects of C++ projects.
+ */
+package org.gradle.nativecode.language.cpp;
diff --git a/subprojects/cpp/src/main/groovy/org/gradle/nativecode/language/cpp/plugins/CppExeConventionPlugin.groovy b/subprojects/cpp/src/main/groovy/org/gradle/nativecode/language/cpp/plugins/CppExeConventionPlugin.groovy
new file mode 100644
index 0000000..88e79f7
--- /dev/null
+++ b/subprojects/cpp/src/main/groovy/org/gradle/nativecode/language/cpp/plugins/CppExeConventionPlugin.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.nativecode.language.cpp.plugins
+
+import org.gradle.api.Incubating
+import org.gradle.api.Project
+import org.gradle.api.Plugin
+
+import org.gradle.api.internal.artifacts.publish.DefaultPublishArtifact
+import org.gradle.api.internal.plugins.DefaultArtifactPublicationSet
+import org.gradle.configuration.project.ProjectConfigurationActionContainer
+
+import javax.inject.Inject
+
+/**
+ * A convention-based plugin that automatically adds a single C++ source set named "main" and wires it into a {@link org.gradle.nativecode.base.Executable} named "main".
+ */
+ at Incubating
+class CppExeConventionPlugin implements Plugin<Project> {
+    private final ProjectConfigurationActionContainer configureActions
+
+    @Inject
+    CppExeConventionPlugin(ProjectConfigurationActionContainer configureActions) {
+        this.configureActions = configureActions
+    }
+
+    void apply(Project project) {
+        project.plugins.apply(CppPlugin)
+        
+        project.with {
+            sources {
+                main {}
+            }
+            executables {
+                main {
+                    baseName = project.name
+                    source sources.main.cpp
+                }
+            }
+        }
+
+        configureActions.add {
+            project.with {
+                def exeArtifact = new DefaultPublishArtifact(
+                        archivesBaseName, // name
+                        "exe", // ext
+                        "exe", // type
+                        null, // classifier
+                        null, // date
+
+                        // needs to be more general and not peer into the spec
+                        binaries.mainExecutable.outputFile,
+                        binaries.mainExecutable
+                )
+
+                extensions.getByType(DefaultArtifactPublicationSet).addCandidate(exeArtifact)
+            }
+        }
+    }
+
+}
\ No newline at end of file
diff --git a/subprojects/cpp/src/main/groovy/org/gradle/nativecode/language/cpp/plugins/CppLangPlugin.groovy b/subprojects/cpp/src/main/groovy/org/gradle/nativecode/language/cpp/plugins/CppLangPlugin.groovy
new file mode 100644
index 0000000..60e487e
--- /dev/null
+++ b/subprojects/cpp/src/main/groovy/org/gradle/nativecode/language/cpp/plugins/CppLangPlugin.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.nativecode.language.cpp.plugins
+import org.gradle.api.Incubating
+import org.gradle.api.Plugin
+import org.gradle.api.internal.project.ProjectInternal
+import org.gradle.internal.reflect.Instantiator
+import org.gradle.language.base.plugins.LanguageBasePlugin
+import org.gradle.nativecode.base.NativeBinary
+import org.gradle.nativecode.base.NativeDependencySet
+import org.gradle.nativecode.base.SharedLibraryBinary
+import org.gradle.nativecode.base.internal.NativeBinaryInternal
+import org.gradle.nativecode.language.cpp.CppSourceSet
+import org.gradle.nativecode.language.cpp.tasks.CppCompile
+
+import javax.inject.Inject
+
+ at Incubating
+class CppLangPlugin implements Plugin<ProjectInternal> {
+    private final Instantiator instantiator;
+
+    @Inject
+    public CppLangPlugin(Instantiator instantiator) {
+        this.instantiator = instantiator;
+    }
+
+    void apply(ProjectInternal project) {
+        project.getPlugins().apply(LanguageBasePlugin.class);
+
+        project.binaries.withType(NativeBinary) { NativeBinaryInternal binary ->
+            binary.source.withType(CppSourceSet).all { CppSourceSet sourceSet ->
+                def compileTask = createCompileTask(project, binary, sourceSet)
+                binary.builderTask.source compileTask.outputs.files.asFileTree.matching { include '**/*.obj', '**/*.o' }
+            }
+        }
+    }
+
+    private def createCompileTask(ProjectInternal project, NativeBinaryInternal binary, def sourceSet) {
+        def compileTask = project.task(binary.namingScheme.getTaskName("compile", sourceSet.fullName), type: CppCompile) {
+            description = "Compiles the $sourceSet sources of $binary"
+        }
+
+        compileTask.toolChain = binary.toolChain
+        compileTask.positionIndependentCode = binary instanceof SharedLibraryBinary
+
+        compileTask.includes sourceSet.exportedHeaders
+        compileTask.source sourceSet.source
+        binary.libs.each { NativeDependencySet deps ->
+            compileTask.includes deps.includeRoots
+        }
+
+        compileTask.conventionMapping.objectFileDir = { project.file("${project.buildDir}/objectFiles/${binary.namingScheme.outputDirectoryBase}/${sourceSet.fullName}") }
+        compileTask.conventionMapping.macros = { binary.macros }
+        compileTask.conventionMapping.compilerArgs = { binary.compilerArgs }
+
+        compileTask
+    }
+}
\ No newline at end of file
diff --git a/subprojects/cpp/src/main/groovy/org/gradle/nativecode/language/cpp/plugins/CppLibConventionPlugin.groovy b/subprojects/cpp/src/main/groovy/org/gradle/nativecode/language/cpp/plugins/CppLibConventionPlugin.groovy
new file mode 100644
index 0000000..de8a3c6
--- /dev/null
+++ b/subprojects/cpp/src/main/groovy/org/gradle/nativecode/language/cpp/plugins/CppLibConventionPlugin.groovy
@@ -0,0 +1,84 @@
+/*
+ * Copyright 2011 the original author or authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.gradle.nativecode.language.cpp.plugins
+
+import org.gradle.api.Incubating
+import org.gradle.api.Project
+import org.gradle.api.Plugin
+import org.gradle.api.tasks.bundling.Zip
+
+import org.gradle.api.internal.artifacts.publish.DefaultPublishArtifact
+import org.gradle.api.internal.artifacts.publish.ArchivePublishArtifact
+import org.gradle.api.internal.plugins.DefaultArtifactPublicationSet
+import org.gradle.configuration.project.ProjectConfigurationActionContainer
+
+import javax.inject.Inject
+
+/**
+ * A convention-based plugin that automatically adds a single C++ source set named "main" and wires it into a {@link org.gradle.nativecode.base.Library} named "main".
+ */
+ at Incubating
+class CppLibConventionPlugin implements Plugin<Project> {
+    private final ProjectConfigurationActionContainer configureActions
+
+    @Inject
+    CppLibConventionPlugin(ProjectConfigurationActionContainer configureActions) {
+        this.configureActions = configureActions
+    }
+
+    void apply(Project project) {
+        project.plugins.apply(CppPlugin)
+
+        project.with {
+            sources {
+                main {}
+            }
+            libraries {
+                main {
+                    baseName = project.name
+                    source sources.main.cpp
+                }
+            }
+        }
+
+        configureActions.add {
+            project.with {
+                def libArtifact = new DefaultPublishArtifact(
+                    archivesBaseName, // name
+                    "so", // ext
+                    "so", // type
+                    "so", // classifier
+                    null, // date
+
+                    // needs to be more general and not peer into the spec
+                    binaries.mainSharedLibrary.outputFile,
+                    binaries.mainSharedLibrary
+                )
+
+                task("assembleHeaders", type: Zip) {
+                    from libraries.main.headers
+                    classifier = "headers"
+                }
+
+                def headerArtifact = new ArchivePublishArtifact(assembleHeaders)
+
+                extensions.getByType(DefaultArtifactPublicationSet).addCandidate(libArtifact)
+                extensions.getByType(DefaultArtifactPublicationSet).addCandidate(headerArtifact)
+            }
+        }
+    }
+
+}
\ No newline at end of file
diff --git a/subprojects/cpp/src/main/groovy/org/gradle/nativecode/language/cpp/plugins/CppPlugin.groovy b/subprojects/cpp/src/main/groovy/org/gradle/nativecode/language/cpp/plugins/CppPlugin.groovy
new file mode 100644
index 0000000..3ad3588
--- /dev/null
+++ b/subprojects/cpp/src/main/groovy/org/gradle/nativecode/language/cpp/plugins/CppPlugin.groovy
@@ -0,0 +1,125 @@
+/*
+ * Copyright 2011 the original author or authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.gradle.nativecode.language.cpp.plugins
+import org.gradle.api.Action
+import org.gradle.api.Incubating
+import org.gradle.api.Plugin
+import org.gradle.api.internal.file.FileResolver
+import org.gradle.api.internal.project.ProjectInternal
+import org.gradle.internal.reflect.Instantiator
+import org.gradle.language.base.FunctionalSourceSet
+import org.gradle.language.base.ProjectSourceSet
+import org.gradle.nativecode.base.ExecutableBinary
+import org.gradle.nativecode.base.NativeBinary
+import org.gradle.nativecode.base.plugins.NativeBinariesPlugin
+import org.gradle.nativecode.base.tasks.CreateStaticLibrary
+import org.gradle.nativecode.base.tasks.LinkExecutable
+import org.gradle.nativecode.base.tasks.LinkSharedLibrary
+import org.gradle.nativecode.language.asm.AssemblerSourceSet
+import org.gradle.nativecode.language.asm.internal.DefaultAssemblerSourceSet
+import org.gradle.nativecode.language.asm.plugins.AssemblerLangPlugin
+import org.gradle.nativecode.language.c.CSourceSet
+import org.gradle.nativecode.language.c.internal.DefaultCSourceSet
+import org.gradle.nativecode.language.c.plugins.CLangPlugin
+import org.gradle.nativecode.language.cpp.CppSourceSet
+import org.gradle.nativecode.language.cpp.internal.DefaultCppSourceSet
+import org.gradle.nativecode.language.cpp.tasks.CppCompile
+import org.gradle.nativecode.toolchain.plugins.GppCompilerPlugin
+import org.gradle.nativecode.toolchain.plugins.MicrosoftVisualCppPlugin
+
+import javax.inject.Inject
+/**
+ * A plugin for projects wishing to build custom components from C++ sources.
+ * <p>Automatically includes {@link MicrosoftVisualCppPlugin} and {@link GppCompilerPlugin} for core toolchain support.</p>
+ *
+ * <p>
+ *     For each {@link NativeBinary} found, this plugin will:
+ *     <ul>
+ *         <li>Create a {@link CppCompile} task named "compile${binary-name}" to compile the C++ sources.</li>
+ *         <li>Create a {@link LinkExecutable} or {@link LinkSharedLibrary} task named "link${binary-name}
+ *             or a {@link CreateStaticLibrary} task name "create${binary-name}" to create the binary artifact.</li>
+ *         <li>Create an InstallTask named "install${Binary-name}" to install any {@link ExecutableBinary} artifact.
+ *     </ul>
+ * </p>
+ */
+ at Incubating
+class CppPlugin implements Plugin<ProjectInternal> {
+    private final Instantiator instantiator;
+    private final FileResolver fileResolver;
+
+    @Inject
+    public CppPlugin(Instantiator instantiator, FileResolver fileResolver) {
+        this.instantiator = instantiator;
+        this.fileResolver = fileResolver;
+    }
+
+    void apply(ProjectInternal project) {
+        project.plugins.apply(NativeBinariesPlugin)
+        project.plugins.apply(MicrosoftVisualCppPlugin)
+        project.plugins.apply(GppCompilerPlugin)
+
+        ProjectSourceSet projectSourceSet = project.getExtensions().getByType(ProjectSourceSet.class);
+        projectSourceSet.all(new Action<FunctionalSourceSet>() {
+            public void execute(final FunctionalSourceSet functionalSourceSet) {
+                applyCppConventions(project, functionalSourceSet)
+                applyCConventions(project, functionalSourceSet)
+                applyAssemblerConventions(project, functionalSourceSet)
+            }
+        });
+
+        project.plugins.apply(CppLangPlugin)
+        project.plugins.apply(CLangPlugin)
+        project.plugins.apply(AssemblerLangPlugin)
+    }
+
+    private void applyCppConventions(ProjectInternal project, FunctionalSourceSet functionalSourceSet) {
+        // Defaults for all cpp source sets
+        functionalSourceSet.withType(CppSourceSet).all(new Action<CppSourceSet>() {
+            void execute(CppSourceSet sourceSet) {
+                sourceSet.exportedHeaders.srcDir "src/${functionalSourceSet.name}/headers"
+                sourceSet.source.srcDir "src/${functionalSourceSet.name}/cpp"
+            }
+        })
+
+        // Create a single C++ source set
+        functionalSourceSet.add(instantiator.newInstance(DefaultCppSourceSet.class, "cpp", functionalSourceSet, project));
+    }
+
+    private void applyCConventions(ProjectInternal project, FunctionalSourceSet functionalSourceSet) {
+        // Defaults for all c source sets
+        functionalSourceSet.withType(CSourceSet).all(new Action<CSourceSet>() {
+            void execute(CSourceSet sourceSet) {
+                sourceSet.exportedHeaders.srcDir "src/${functionalSourceSet.name}/headers"
+                sourceSet.source.srcDir "src/${functionalSourceSet.name}/c"
+            }
+        })
+
+        // Create a single C source set
+        functionalSourceSet.add(instantiator.newInstance(DefaultCSourceSet.class, "c", functionalSourceSet, project));
+    }
+
+    private void applyAssemblerConventions(ProjectInternal project, FunctionalSourceSet functionalSourceSet) {
+        // Defaults for all assembler source sets
+        functionalSourceSet.withType(AssemblerSourceSet).all(new Action<AssemblerSourceSet>() {
+            void execute(AssemblerSourceSet sourceSet) {
+                sourceSet.source.srcDir "src/${functionalSourceSet.name}/asm"
+            }
+        })
+
+        // Create a single assembler source set
+        functionalSourceSet.add(instantiator.newInstance(DefaultAssemblerSourceSet.class, "asm", functionalSourceSet, project));
+    }
+}
\ No newline at end of file
diff --git a/subprojects/cpp/src/main/groovy/org/gradle/nativecode/language/cpp/plugins/package-info.java b/subprojects/cpp/src/main/groovy/org/gradle/nativecode/language/cpp/plugins/package-info.java
new file mode 100644
index 0000000..c692538
--- /dev/null
+++ b/subprojects/cpp/src/main/groovy/org/gradle/nativecode/language/cpp/plugins/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.
+ */
+
+/**
+ * Plugins for buildings C++ projects.
+ */
+package org.gradle.nativecode.language.cpp.plugins;
diff --git a/subprojects/cpp/src/main/groovy/org/gradle/nativecode/language/cpp/tasks/CppCompile.groovy b/subprojects/cpp/src/main/groovy/org/gradle/nativecode/language/cpp/tasks/CppCompile.groovy
new file mode 100644
index 0000000..4ee53a2
--- /dev/null
+++ b/subprojects/cpp/src/main/groovy/org/gradle/nativecode/language/cpp/tasks/CppCompile.groovy
@@ -0,0 +1,40 @@
+/*
+ * Copyright 2012 the original author or authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.gradle.nativecode.language.cpp.tasks
+
+import org.gradle.api.Incubating
+import org.gradle.api.tasks.WorkResult
+import org.gradle.nativecode.base.internal.ToolChainInternal
+import org.gradle.nativecode.language.base.tasks.AbstractNativeCompileTask
+import org.gradle.nativecode.language.cpp.internal.DefaultCppCompileSpec
+import org.gradle.nativecode.language.base.internal.NativeCompileSpec
+
+/**
+ * Compiles C++ source files into object files.
+ */
+ at Incubating
+class CppCompile  extends AbstractNativeCompileTask {
+    @Override
+    protected NativeCompileSpec createCompileSpec() {
+        new DefaultCppCompileSpec()
+    }
+
+    @Override
+    protected WorkResult execute(ToolChainInternal toolChain, NativeCompileSpec spec) {
+        return toolChain.createCppCompiler().execute(spec)
+    }
+}
\ No newline at end of file
diff --git a/subprojects/cpp/src/main/groovy/org/gradle/nativecode/language/cpp/tasks/package-info.java b/subprojects/cpp/src/main/groovy/org/gradle/nativecode/language/cpp/tasks/package-info.java
new file mode 100644
index 0000000..f289fcd
--- /dev/null
+++ b/subprojects/cpp/src/main/groovy/org/gradle/nativecode/language/cpp/tasks/package-info.java
@@ -0,0 +1,20 @@
+/*
+ * Copyright 2013 the original author or authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+/**
+ * Tasks for building C++ projects.
+ */
+package org.gradle.nativecode.language.cpp.tasks;
diff --git a/subprojects/cpp/src/main/groovy/org/gradle/nativecode/toolchain/ConfigurableToolChain.java b/subprojects/cpp/src/main/groovy/org/gradle/nativecode/toolchain/ConfigurableToolChain.java
new file mode 100644
index 0000000..c8d7597
--- /dev/null
+++ b/subprojects/cpp/src/main/groovy/org/gradle/nativecode/toolchain/ConfigurableToolChain.java
@@ -0,0 +1,65 @@
+/*
+ * Copyright 2013 the original author or authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.gradle.nativecode.toolchain;
+
+import org.gradle.api.Incubating;
+import org.gradle.nativecode.base.ToolChain;
+
+import java.io.File;
+import java.util.List;
+
+/**
+ * A tool chain that allows the individual tools to be configured.
+ */
+ at Incubating
+public interface ConfigurableToolChain extends ToolChain {
+    /**
+     * The paths setting required for executing the tool chain.
+     */
+    List<File> getPaths();
+    // TODO:DAZ Add a setter
+
+    /**
+     * Add an entry or entries to the tool chain path.
+     */
+    void path(Object... pathEntry);
+
+    /**
+     * The C++ compiler.
+     */
+    Tool getCCompiler();
+
+    /**
+     * The C compiler.
+     */
+    Tool getCppCompiler();
+
+    /**
+     * The assembler.
+     */
+    Tool getAssembler();
+
+    /**
+     * The linker.
+     */
+    Tool getLinker();
+
+    /**
+     * The static library archiver.
+     */
+    Tool getStaticLibArchiver();
+}
diff --git a/subprojects/cpp/src/main/groovy/org/gradle/nativecode/toolchain/Gcc.java b/subprojects/cpp/src/main/groovy/org/gradle/nativecode/toolchain/Gcc.java
new file mode 100644
index 0000000..a0e86aa
--- /dev/null
+++ b/subprojects/cpp/src/main/groovy/org/gradle/nativecode/toolchain/Gcc.java
@@ -0,0 +1,26 @@
+/*
+ * Copyright 2013 the original author or authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.gradle.nativecode.toolchain;
+
+import org.gradle.api.Incubating;
+
+/**
+ * The Visual C++ tool chain.
+ */
+ at Incubating
+public interface Gcc extends ConfigurableToolChain {
+}
diff --git a/subprojects/cpp/src/main/groovy/org/gradle/nativecode/toolchain/Tool.java b/subprojects/cpp/src/main/groovy/org/gradle/nativecode/toolchain/Tool.java
new file mode 100644
index 0000000..819535b
--- /dev/null
+++ b/subprojects/cpp/src/main/groovy/org/gradle/nativecode/toolchain/Tool.java
@@ -0,0 +1,36 @@
+/*
+ * Copyright 2013 the original author or authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.gradle.nativecode.toolchain;
+
+import org.gradle.api.Incubating;
+
+/**
+ * An executable tool that forms part of a tool chain.
+ */
+ at Incubating
+public interface Tool {
+    /**
+     * The name of the executable file for this tool.
+     */
+    String getExe();
+
+    /**
+     * Set the name of the executable file for this tool.
+     * The executable will be located in the tool chain path.
+     */
+    void setExe(String file);
+}
diff --git a/subprojects/cpp/src/main/groovy/org/gradle/nativecode/toolchain/VisualCpp.java b/subprojects/cpp/src/main/groovy/org/gradle/nativecode/toolchain/VisualCpp.java
new file mode 100644
index 0000000..8d70545
--- /dev/null
+++ b/subprojects/cpp/src/main/groovy/org/gradle/nativecode/toolchain/VisualCpp.java
@@ -0,0 +1,38 @@
+/*
+ * Copyright 2013 the original author or authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.gradle.nativecode.toolchain;
+
+import org.gradle.api.Incubating;
+
+import java.io.File;
+
+/**
+ * The Visual C++ tool chain.
+ */
+ at Incubating
+public interface VisualCpp extends ConfigurableToolChain {
+    /**
+     * The directory where Visual Studio or Visual C++ is installed.
+     */
+    File getInstallDir();
+
+    /**
+     * The directory where Visual Studio or Visual C++ is installed.
+     * Setting the installDir will overwrite any paths added previously via {@link ConfigurableToolChain#path(Object)}.
+     */
+    void setInstallDir(Object installDir);
+}
diff --git a/subprojects/cpp/src/main/groovy/org/gradle/nativecode/toolchain/internal/CommandLineCompilerArgumentsToOptionFile.java b/subprojects/cpp/src/main/groovy/org/gradle/nativecode/toolchain/internal/CommandLineCompilerArgumentsToOptionFile.java
new file mode 100644
index 0000000..9f721fc
--- /dev/null
+++ b/subprojects/cpp/src/main/groovy/org/gradle/nativecode/toolchain/internal/CommandLineCompilerArgumentsToOptionFile.java
@@ -0,0 +1,58 @@
+/*
+ * Copyright 2012 the original author or authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.gradle.nativecode.toolchain.internal;
+
+import org.gradle.api.Transformer;
+import org.gradle.api.UncheckedIOException;
+import org.gradle.api.internal.tasks.compile.ArgCollector;
+import org.gradle.api.internal.tasks.compile.ArgWriter;
+import org.gradle.api.internal.tasks.compile.CompileSpecToArguments;
+import org.gradle.nativecode.base.internal.BinaryToolSpec;
+import org.gradle.util.GFileUtils;
+
+import java.io.File;
+import java.io.IOException;
+import java.io.PrintWriter;
+
+public class CommandLineCompilerArgumentsToOptionFile<T extends BinaryToolSpec> implements CompileSpecToArguments<T> {
+
+    private final Transformer<ArgWriter, PrintWriter> argWriterFactory;
+    private final CompileSpecToArguments<T> toArguments;
+
+    public CommandLineCompilerArgumentsToOptionFile(Transformer<ArgWriter, PrintWriter> argWriterFactory, CompileSpecToArguments<T> toArguments) {
+        this.argWriterFactory = argWriterFactory;
+        this.toArguments = toArguments;
+    }
+
+    public void collectArguments(T spec, ArgCollector collector) {
+        GFileUtils.mkdirs(spec.getTempDir());
+        File optionsFile = new File(spec.getTempDir(), "compiler-options.txt");
+        try {
+            PrintWriter writer = new PrintWriter(optionsFile);
+            ArgWriter argWriter = argWriterFactory.transform(writer);
+            try {
+                toArguments.collectArguments(spec, argWriter);
+            } finally {
+                writer.close();
+            }
+        } catch (IOException e) {
+            throw new UncheckedIOException(String.format("Could not write compiler options file '%s'.", optionsFile.getAbsolutePath()), e);
+        }
+
+        collector.args(String.format("@%s", optionsFile.getAbsolutePath()));
+    }
+}
diff --git a/subprojects/cpp/src/main/groovy/org/gradle/nativecode/toolchain/internal/CommandLineTool.java b/subprojects/cpp/src/main/groovy/org/gradle/nativecode/toolchain/internal/CommandLineTool.java
new file mode 100755
index 0000000..5a77d5e
--- /dev/null
+++ b/subprojects/cpp/src/main/groovy/org/gradle/nativecode/toolchain/internal/CommandLineTool.java
@@ -0,0 +1,98 @@
+/*
+ * Copyright 2013 the original author or authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.gradle.nativecode.toolchain.internal;
+
+import com.google.common.base.Joiner;
+import org.gradle.api.GradleException;
+import org.gradle.api.internal.tasks.SimpleWorkResult;
+import org.gradle.api.internal.tasks.compile.CompileSpec;
+import org.gradle.api.internal.tasks.compile.CompileSpecToArguments;
+import org.gradle.api.internal.tasks.compile.ExecSpecBackedArgCollector;
+import org.gradle.api.tasks.WorkResult;
+import org.gradle.internal.Factory;
+import org.gradle.internal.os.OperatingSystem;
+import org.gradle.process.internal.ExecAction;
+import org.gradle.process.internal.ExecException;
+import org.gradle.util.GFileUtils;
+import org.gradle.util.GUtil;
+
+import java.io.File;
+import java.util.HashMap;
+import java.util.List;
+import java.util.Map;
+
+public class CommandLineTool<T extends CompileSpec> {
+    private final String action;
+    private final File executable;
+    private final Factory<ExecAction> execActionFactory;
+    private final Map<String, String> environment = new HashMap<String, String>();
+    private CompileSpecToArguments<T> toArguments;
+    private File workDir;
+    private String path;
+
+    public CommandLineTool(String action, File executable, Factory<ExecAction> execActionFactory) {
+        this.action = action;
+        this.executable = executable;
+        this.execActionFactory = execActionFactory;
+    }
+
+    public CommandLineTool<T> inWorkDirectory(File workDir) {
+        GFileUtils.mkdirs(workDir);
+        this.workDir = workDir;
+        return this;
+    }
+
+    public CommandLineTool<T> withPath(List<File> pathEntries) {
+        path = Joiner.on(File.pathSeparator).join(pathEntries);
+        return this;
+    }
+
+    public CommandLineTool<T> withEnvironment(Map<String, String> environment) {
+        this.environment.putAll(environment);
+        return this;
+    }
+
+    public CommandLineTool<T> withArguments(CompileSpecToArguments<T> arguments) {
+        this.toArguments = arguments;
+        return this;
+    }
+
+    public WorkResult execute(T spec) {
+        ExecAction compiler = execActionFactory.create();
+        compiler.executable(executable);
+        if (workDir != null) {
+            compiler.workingDir(workDir);
+        }
+
+        toArguments.collectArguments(spec, new ExecSpecBackedArgCollector(compiler));
+
+        if (GUtil.isTrue(path)) {
+            String pathVar = OperatingSystem.current().getPathVar();
+            String compilerPath = path + File.pathSeparator + System.getenv(pathVar);
+            compiler.environment(pathVar, compilerPath);
+        }
+
+        compiler.environment(environment);
+
+        try {
+            compiler.execute();
+        } catch (ExecException e) {
+            throw new GradleException(String.format("%s failed; see the error output for details.", action), e);
+        }
+        return new SimpleWorkResult(true);
+    }
+}
diff --git a/subprojects/cpp/src/main/groovy/org/gradle/nativecode/toolchain/internal/ToolRegistry.java b/subprojects/cpp/src/main/groovy/org/gradle/nativecode/toolchain/internal/ToolRegistry.java
new file mode 100644
index 0000000..dca2914
--- /dev/null
+++ b/subprojects/cpp/src/main/groovy/org/gradle/nativecode/toolchain/internal/ToolRegistry.java
@@ -0,0 +1,78 @@
+/*
+ * Copyright 2013 the original author or authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.gradle.nativecode.toolchain.internal;
+
+import org.gradle.internal.os.OperatingSystem;
+
+import java.io.File;
+import java.util.*;
+
+public class ToolRegistry {
+    private final Map<ToolType, String> executableNames = new HashMap<ToolType, String>();
+    private final Map<String, File> executables = new HashMap<String, File>();
+    private final List<File> pathEntries = new ArrayList<File>();
+
+    private final OperatingSystem operatingSystem;
+
+    public ToolRegistry(OperatingSystem operatingSystem) {
+        this.operatingSystem = operatingSystem;
+    }
+
+    public List<File> getPath() {
+        return pathEntries;
+    }
+
+    public void setPath(List<File> pathEntries) {
+        this.pathEntries.clear();
+        this.pathEntries.addAll(pathEntries);
+        executables.clear();
+    }
+
+    public void path(File pathEntry) {
+        pathEntries.add(pathEntry);
+        executables.clear();
+    }
+
+    public String getExeName(ToolType key) {
+        return executableNames.get(key);
+    }
+
+    public void setExeName(ToolType key, String name) {
+        executableNames.put(key, name);
+    }
+
+    public File locate(ToolType key) {
+        String exeName = executableNames.get(key);
+        if (executables.containsKey(exeName)) {
+            return executables.get(exeName);
+        }
+        File exe = findExecutable(operatingSystem, exeName);
+        executables.put(exeName, exe);
+        return exe;
+    }
+
+    protected File findExecutable(OperatingSystem operatingSystem, String name) {
+        String exeName = operatingSystem.getExecutableName(name);
+        for (File pathEntry : pathEntries) {
+            File candidate = new File(pathEntry, exeName);
+            if (candidate.isFile()) {
+                return candidate;
+            }
+        }
+        return operatingSystem.findInPath(name);
+    }
+}
diff --git a/subprojects/cpp/src/main/groovy/org/gradle/nativecode/toolchain/internal/ToolType.java b/subprojects/cpp/src/main/groovy/org/gradle/nativecode/toolchain/internal/ToolType.java
new file mode 100644
index 0000000..74f9eaf
--- /dev/null
+++ b/subprojects/cpp/src/main/groovy/org/gradle/nativecode/toolchain/internal/ToolType.java
@@ -0,0 +1,42 @@
+/*
+ * Copyright 2013 the original author or authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.gradle.nativecode.toolchain.internal;
+
+import org.gradle.util.GUtil;
+
+public enum ToolType {
+    CPP_COMPILER("C++ compiler"),
+    C_COMPILER("C compiler"),
+    ASSEMBLER("Assembler"),
+    LINKER("Linker"),
+    STATIC_LIB_ARCHIVER("Static library archiver");
+
+    private final String toolName;
+
+    ToolType(String toolName) {
+        this.toolName = toolName;
+    }
+
+    public String getToolName() {
+        return toolName;
+    }
+
+    @Override
+    public String toString() {
+        return GUtil.toLowerCamelCase(name());
+    }
+}
diff --git a/subprojects/cpp/src/main/groovy/org/gradle/nativecode/toolchain/internal/gpp/ArStaticLibraryArchiver.java b/subprojects/cpp/src/main/groovy/org/gradle/nativecode/toolchain/internal/gpp/ArStaticLibraryArchiver.java
new file mode 100755
index 0000000..e07e814
--- /dev/null
+++ b/subprojects/cpp/src/main/groovy/org/gradle/nativecode/toolchain/internal/gpp/ArStaticLibraryArchiver.java
@@ -0,0 +1,52 @@
+/*
+ * Copyright 2013 the original author or authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.gradle.nativecode.toolchain.internal.gpp;
+
+import org.gradle.api.internal.tasks.compile.ArgCollector;
+import org.gradle.api.internal.tasks.compile.CompileSpecToArguments;
+import org.gradle.api.internal.tasks.compile.Compiler;
+import org.gradle.api.tasks.WorkResult;
+import org.gradle.nativecode.base.internal.StaticLibraryArchiverSpec;
+import org.gradle.nativecode.toolchain.internal.CommandLineTool;
+
+import java.io.File;
+
+/**
+ * A static library linker based on the GNU 'ar' utility
+ */
+
+class ArStaticLibraryArchiver implements Compiler<StaticLibraryArchiverSpec> {
+    private final CommandLineTool<StaticLibraryArchiverSpec> commandLineTool;
+
+    public ArStaticLibraryArchiver(CommandLineTool<StaticLibraryArchiverSpec> commandLineTool) {
+        this.commandLineTool = commandLineTool.withArguments(new ArchiverSpecToArguments());
+    }
+
+    public WorkResult execute(StaticLibraryArchiverSpec spec) {
+        return commandLineTool.execute(spec);
+    }
+
+    private static class ArchiverSpecToArguments implements CompileSpecToArguments<StaticLibraryArchiverSpec> {
+        public void collectArguments(StaticLibraryArchiverSpec spec, ArgCollector collector) {
+            collector.args("-rc", spec.getOutputFile().getAbsolutePath());
+            collector.args(spec.getArgs());
+            for (File file : spec.getSource()) {
+                collector.args(file.getAbsolutePath());
+            }
+        }
+    }
+}
diff --git a/subprojects/cpp/src/main/groovy/org/gradle/nativecode/toolchain/internal/gpp/Assembler.java b/subprojects/cpp/src/main/groovy/org/gradle/nativecode/toolchain/internal/gpp/Assembler.java
new file mode 100755
index 0000000..cec664a
--- /dev/null
+++ b/subprojects/cpp/src/main/groovy/org/gradle/nativecode/toolchain/internal/gpp/Assembler.java
@@ -0,0 +1,65 @@
+/*
+ * Copyright 2013 the original author or authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.gradle.nativecode.toolchain.internal.gpp;
+
+import org.gradle.api.internal.tasks.SimpleWorkResult;
+import org.gradle.api.internal.tasks.compile.ArgCollector;
+import org.gradle.api.internal.tasks.compile.CompileSpecToArguments;
+import org.gradle.api.internal.tasks.compile.Compiler;
+import org.gradle.api.tasks.WorkResult;
+import org.gradle.nativecode.language.asm.internal.AssembleSpec;
+import org.gradle.nativecode.toolchain.internal.CommandLineTool;
+
+import java.io.File;
+
+class Assembler implements Compiler<AssembleSpec> {
+
+    private final CommandLineTool<AssembleSpec> commandLineTool;
+
+    public Assembler(CommandLineTool<AssembleSpec> commandLineTool) {
+        this.commandLineTool = commandLineTool;
+    }
+
+    public WorkResult execute(AssembleSpec spec) {
+        boolean didWork = false;
+        CommandLineTool<AssembleSpec> commandLineAssembler = commandLineTool.inWorkDirectory(spec.getObjectFileDir());
+        for (File sourceFile : spec.getSource()) {
+            WorkResult result = commandLineAssembler.withArguments(new AssemblerSpecToArguments(sourceFile)).execute(spec);
+            didWork = didWork || result.getDidWork();
+        }
+        return new SimpleWorkResult(didWork);
+    }
+
+
+    private static class AssemblerSpecToArguments implements CompileSpecToArguments<AssembleSpec> {
+        private final File inputFile;
+        private final String outputFileName;
+
+        public AssemblerSpecToArguments(File inputFile) {
+            this.inputFile = inputFile;
+            this.outputFileName = inputFile.getName() + ".o";
+        }
+
+        public void collectArguments(AssembleSpec spec, ArgCollector collector) {
+            for (String rawArg : spec.getArgs()) {
+                collector.args(rawArg);
+            }
+            collector.args("-o", outputFileName);
+            collector.args(inputFile.getAbsolutePath());
+        }
+    }
+}
diff --git a/subprojects/cpp/src/main/groovy/org/gradle/nativecode/toolchain/internal/gpp/CCompiler.java b/subprojects/cpp/src/main/groovy/org/gradle/nativecode/toolchain/internal/gpp/CCompiler.java
new file mode 100755
index 0000000..7ad2fd2
--- /dev/null
+++ b/subprojects/cpp/src/main/groovy/org/gradle/nativecode/toolchain/internal/gpp/CCompiler.java
@@ -0,0 +1,50 @@
+/*
+ * Copyright 2013 the original author or authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.gradle.nativecode.toolchain.internal.gpp;
+
+import org.gradle.api.internal.tasks.compile.ArgCollector;
+import org.gradle.api.internal.tasks.compile.Compiler;
+import org.gradle.api.tasks.WorkResult;
+import org.gradle.nativecode.language.c.internal.CCompileSpec;
+import org.gradle.nativecode.toolchain.internal.CommandLineTool;
+
+class CCompiler implements Compiler<CCompileSpec> {
+
+    private final CommandLineTool<CCompileSpec> commandLineTool;
+
+    public CCompiler(CommandLineTool<CCompileSpec> commandLineTool, boolean useCommandFile) {
+        GccCompileSpecToArguments<CCompileSpec> specToArguments = new GccCompileSpecToArguments<CCompileSpec>(
+                new CCompileOptionsToArguments(),
+                new GccCompileSourcesToArguments<CCompileSpec>(),
+                useCommandFile
+        );
+        this.commandLineTool = commandLineTool.withArguments(specToArguments);
+    }
+
+    public WorkResult execute(CCompileSpec spec) {
+        return commandLineTool.inWorkDirectory(spec.getObjectFileDir()).execute(spec);
+    }
+
+    private static class CCompileOptionsToArguments extends GeneralGccCompileOptionsToArguments<CCompileSpec> {
+        public void collectArguments(CCompileSpec spec, ArgCollector collector) {
+            // C-compiling options
+            collector.args("-x", "c");
+
+            super.collectArguments(spec, collector);
+        }
+    }
+}
diff --git a/subprojects/cpp/src/main/groovy/org/gradle/nativecode/toolchain/internal/gpp/CppCompiler.java b/subprojects/cpp/src/main/groovy/org/gradle/nativecode/toolchain/internal/gpp/CppCompiler.java
new file mode 100755
index 0000000..ccf45e0
--- /dev/null
+++ b/subprojects/cpp/src/main/groovy/org/gradle/nativecode/toolchain/internal/gpp/CppCompiler.java
@@ -0,0 +1,52 @@
+/*
+ * Copyright 2012 the original author or authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.gradle.nativecode.toolchain.internal.gpp;
+
+import org.gradle.api.internal.tasks.compile.ArgCollector;
+import org.gradle.api.internal.tasks.compile.Compiler;
+import org.gradle.api.tasks.WorkResult;
+import org.gradle.nativecode.language.cpp.internal.CppCompileSpec;
+import org.gradle.nativecode.toolchain.internal.CommandLineTool;
+
+class CppCompiler implements Compiler<CppCompileSpec> {
+
+    private final CommandLineTool<CppCompileSpec> commandLineTool;
+
+    public CppCompiler(CommandLineTool<CppCompileSpec> commandLineTool, boolean useCommandFile) {
+        GccCompileSpecToArguments<CppCompileSpec> specToArguments = new GccCompileSpecToArguments<CppCompileSpec>(
+                new CppCompileOptionsToArguments(),
+                new GccCompileSourcesToArguments<CppCompileSpec>(),
+                useCommandFile
+        );
+        this.commandLineTool = commandLineTool.withArguments(specToArguments);
+    }
+
+    public WorkResult execute(CppCompileSpec spec) {
+        return commandLineTool.inWorkDirectory(spec.getObjectFileDir()).execute(spec);
+    }
+
+    // Certain options do not function correctly via an option file, so only use option file for headers and sources
+    private static class CppCompileOptionsToArguments extends GeneralGccCompileOptionsToArguments<CppCompileSpec> {
+        @Override
+        public void collectArguments(CppCompileSpec spec, ArgCollector collector) {
+            // C++-compiling options
+            collector.args("-x", "c++");
+
+            super.collectArguments(spec, collector);
+        }
+    }
+}
diff --git a/subprojects/cpp/src/main/groovy/org/gradle/nativecode/toolchain/internal/gpp/GccCompileSourcesToArguments.java b/subprojects/cpp/src/main/groovy/org/gradle/nativecode/toolchain/internal/gpp/GccCompileSourcesToArguments.java
new file mode 100644
index 0000000..a1059ea
--- /dev/null
+++ b/subprojects/cpp/src/main/groovy/org/gradle/nativecode/toolchain/internal/gpp/GccCompileSourcesToArguments.java
@@ -0,0 +1,34 @@
+/*
+ * Copyright 2013 the original author or authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.gradle.nativecode.toolchain.internal.gpp;
+
+import org.gradle.api.internal.tasks.compile.ArgCollector;
+import org.gradle.api.internal.tasks.compile.CompileSpecToArguments;
+import org.gradle.nativecode.language.base.internal.NativeCompileSpec;
+
+import java.io.File;
+
+class GccCompileSourcesToArguments<T extends NativeCompileSpec> implements CompileSpecToArguments<T> {
+    public void collectArguments(T spec, ArgCollector collector) {
+        for (File file : spec.getIncludeRoots()) {
+            collector.args("-I");
+            collector.args(file.getAbsolutePath());
+        }
+        for (File file : spec.getSource()) {
+            collector.args(file.getAbsolutePath());
+        }
+    }
+}
diff --git a/subprojects/cpp/src/main/groovy/org/gradle/nativecode/toolchain/internal/gpp/GccCompileSpecToArguments.java b/subprojects/cpp/src/main/groovy/org/gradle/nativecode/toolchain/internal/gpp/GccCompileSpecToArguments.java
new file mode 100644
index 0000000..a15b4aa
--- /dev/null
+++ b/subprojects/cpp/src/main/groovy/org/gradle/nativecode/toolchain/internal/gpp/GccCompileSpecToArguments.java
@@ -0,0 +1,51 @@
+/*
+ * Copyright 2012 the original author or authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.gradle.nativecode.toolchain.internal.gpp;
+
+import org.gradle.api.internal.tasks.compile.ArgCollector;
+import org.gradle.api.internal.tasks.compile.ArgWriter;
+import org.gradle.api.internal.tasks.compile.CompileSpecToArguments;
+import org.gradle.nativecode.base.internal.BinaryToolSpec;
+import org.gradle.nativecode.toolchain.internal.CommandLineCompilerArgumentsToOptionFile;
+
+/**
+ * Some GCC options do not function correctly when included in an option file, so only use the option file for include paths and source files.
+ */
+public class GccCompileSpecToArguments<T extends BinaryToolSpec> implements CompileSpecToArguments<T> {
+    private final CompileSpecToArguments<T> optionsToArguments;
+    private final CompileSpecToArguments<T> sourcesToArguments;
+
+    public GccCompileSpecToArguments(CompileSpecToArguments<T> optionsToArguments, CompileSpecToArguments<T> sourcesToArguments, boolean useOptionFile) {
+        this.optionsToArguments = optionsToArguments;
+
+        // Only use an option file for header paths and source files (some other options don't function correctly in option file
+        if (useOptionFile) {
+            this.sourcesToArguments = withOptionFile(sourcesToArguments);
+        } else {
+            this.sourcesToArguments = sourcesToArguments;
+        }
+    }
+
+    private CommandLineCompilerArgumentsToOptionFile<T> withOptionFile(CompileSpecToArguments<T> sourcesToArguments) {
+        return new CommandLineCompilerArgumentsToOptionFile<T>(ArgWriter.unixStyleFactory(), sourcesToArguments);
+    }
+
+    public void collectArguments(T spec, ArgCollector collector) {
+        optionsToArguments.collectArguments(spec, collector);
+        sourcesToArguments.collectArguments(spec, collector);
+    }
+}
diff --git a/subprojects/cpp/src/main/groovy/org/gradle/nativecode/toolchain/internal/gpp/GccToolRegistry.java b/subprojects/cpp/src/main/groovy/org/gradle/nativecode/toolchain/internal/gpp/GccToolRegistry.java
new file mode 100644
index 0000000..ec692b1
--- /dev/null
+++ b/subprojects/cpp/src/main/groovy/org/gradle/nativecode/toolchain/internal/gpp/GccToolRegistry.java
@@ -0,0 +1,49 @@
+/*
+ * Copyright 2013 the original author or authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.gradle.nativecode.toolchain.internal.gpp;
+
+import org.gradle.internal.os.OperatingSystem;
+import org.gradle.nativecode.toolchain.internal.ToolRegistry;
+
+import java.io.File;
+import java.util.Arrays;
+import java.util.List;
+
+public class GccToolRegistry extends ToolRegistry {
+    public GccToolRegistry(OperatingSystem operatingSystem) {
+        super(operatingSystem);
+    }
+
+    @Override
+    protected File findExecutable(OperatingSystem operatingSystem, String name) {
+        List<String> candidates;
+        if (operatingSystem.isWindows()) {
+            // Under Cygwin, g++/gcc is a Cygwin symlink to either g++-3 or g++-4. We can't run g++ directly
+            candidates = Arrays.asList(name + "-4", name + "-3", name);
+        } else {
+            candidates = Arrays.asList(name);
+        }
+        for (String candidate : candidates) {
+            File executable = super.findExecutable(operatingSystem, candidate);
+            if (executable != null) {
+                return executable;
+            }
+        }
+        return null;
+    }
+
+}
diff --git a/subprojects/cpp/src/main/groovy/org/gradle/nativecode/toolchain/internal/gpp/GeneralGccCompileOptionsToArguments.java b/subprojects/cpp/src/main/groovy/org/gradle/nativecode/toolchain/internal/gpp/GeneralGccCompileOptionsToArguments.java
new file mode 100644
index 0000000..95a2e33
--- /dev/null
+++ b/subprojects/cpp/src/main/groovy/org/gradle/nativecode/toolchain/internal/gpp/GeneralGccCompileOptionsToArguments.java
@@ -0,0 +1,40 @@
+/*
+ * Copyright 2013 the original author or authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.gradle.nativecode.toolchain.internal.gpp;
+
+import org.gradle.api.internal.tasks.compile.ArgCollector;
+import org.gradle.api.internal.tasks.compile.CompileSpecToArguments;
+import org.gradle.internal.os.OperatingSystem;
+import org.gradle.nativecode.language.base.internal.NativeCompileSpec;
+
+/**
+ * Maps common options for C/C++ compiling with GCC
+ */
+class GeneralGccCompileOptionsToArguments<T extends NativeCompileSpec> implements CompileSpecToArguments<T> {
+    public void collectArguments(T spec, ArgCollector collector) {
+        for (String macro : spec.getMacros()) {
+            collector.args("-D" + macro);
+        }
+        collector.args(spec.getArgs());
+        collector.args("-c");
+        if (spec.isPositionIndependentCode()) {
+            if (!OperatingSystem.current().isWindows()) {
+                collector.args("-fPIC");
+            }
+        }
+    }
+}
diff --git a/subprojects/cpp/src/main/groovy/org/gradle/nativecode/toolchain/internal/gpp/GppLinker.java b/subprojects/cpp/src/main/groovy/org/gradle/nativecode/toolchain/internal/gpp/GppLinker.java
new file mode 100755
index 0000000..48960ed
--- /dev/null
+++ b/subprojects/cpp/src/main/groovy/org/gradle/nativecode/toolchain/internal/gpp/GppLinker.java
@@ -0,0 +1,80 @@
+/*
+ * Copyright 2013 the original author or authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.gradle.nativecode.toolchain.internal.gpp;
+
+import org.gradle.api.internal.tasks.compile.ArgCollector;
+import org.gradle.api.internal.tasks.compile.ArgWriter;
+import org.gradle.api.internal.tasks.compile.CompileSpecToArguments;
+import org.gradle.api.internal.tasks.compile.Compiler;
+import org.gradle.api.tasks.WorkResult;
+import org.gradle.internal.os.OperatingSystem;
+import org.gradle.nativecode.base.internal.LinkerSpec;
+import org.gradle.nativecode.base.internal.SharedLibraryLinkerSpec;
+import org.gradle.nativecode.toolchain.internal.CommandLineCompilerArgumentsToOptionFile;
+import org.gradle.nativecode.toolchain.internal.CommandLineTool;
+
+import java.io.File;
+
+class GppLinker implements Compiler<LinkerSpec> {
+
+    private final CommandLineTool<LinkerSpec> commandLineTool;
+
+    public GppLinker(CommandLineTool<LinkerSpec> commandLineTool, boolean useCommandFile) {
+        this.commandLineTool = commandLineTool.withArguments(useCommandFile ? viaCommandFile() : withoutCommandFile());
+    }
+
+    private static GppLinkerSpecToArguments withoutCommandFile() {
+        return new GppLinkerSpecToArguments();
+    }
+
+    private static CommandLineCompilerArgumentsToOptionFile<LinkerSpec> viaCommandFile() {
+        return new CommandLineCompilerArgumentsToOptionFile<LinkerSpec>(
+            ArgWriter.unixStyleFactory(), new GppLinkerSpecToArguments()
+        );
+    }
+
+    public WorkResult execute(LinkerSpec spec) {
+        return commandLineTool.execute(spec);
+    }
+
+    private static class GppLinkerSpecToArguments implements CompileSpecToArguments<LinkerSpec> {
+
+        public void collectArguments(LinkerSpec spec, ArgCollector collector) {
+            for (String rawArg : spec.getArgs()) {
+                collector.args("-Xlinker", rawArg);
+            }
+            if (spec instanceof SharedLibraryLinkerSpec) {
+                collector.args("-shared");
+                if (!OperatingSystem.current().isWindows()) {
+                    String installName = ((SharedLibraryLinkerSpec) spec).getInstallName();
+                    if (OperatingSystem.current().isMacOsX()) {
+                        collector.args("-Wl,-install_name," + installName);
+                    } else {
+                        collector.args("-Wl,-soname," + installName);
+                    }
+                }
+            }
+            collector.args("-o", spec.getOutputFile().getAbsolutePath());
+            for (File file : spec.getSource()) {
+                collector.args(file.getAbsolutePath());
+            }
+            for (File file : spec.getLibs()) {
+                collector.args(file.getAbsolutePath());
+            }
+        }
+    }
+}
diff --git a/subprojects/cpp/src/main/groovy/org/gradle/nativecode/toolchain/internal/gpp/GppToolChain.java b/subprojects/cpp/src/main/groovy/org/gradle/nativecode/toolchain/internal/gpp/GppToolChain.java
new file mode 100755
index 0000000..cef886f
--- /dev/null
+++ b/subprojects/cpp/src/main/groovy/org/gradle/nativecode/toolchain/internal/gpp/GppToolChain.java
@@ -0,0 +1,139 @@
+/*
+ * Copyright 2012 the original author or authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.gradle.nativecode.toolchain.internal.gpp;
+
+import org.gradle.api.Transformer;
+import org.gradle.api.internal.file.FileResolver;
+import org.gradle.api.internal.tasks.compile.Compiler;
+import org.gradle.internal.Factory;
+import org.gradle.internal.os.OperatingSystem;
+import org.gradle.nativecode.base.internal.*;
+import org.gradle.nativecode.language.asm.internal.AssembleSpec;
+import org.gradle.nativecode.language.c.internal.CCompileSpec;
+import org.gradle.nativecode.language.cpp.internal.CppCompileSpec;
+import org.gradle.nativecode.toolchain.Gcc;
+import org.gradle.nativecode.toolchain.internal.CommandLineTool;
+import org.gradle.nativecode.toolchain.internal.ToolType;
+import org.gradle.nativecode.toolchain.internal.gpp.version.GppVersionDeterminer;
+import org.gradle.process.internal.ExecAction;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+import java.io.File;
+
+/**
+ * Compiler adapter for GCC.
+ */
+public class GppToolChain extends AbstractToolChain implements Gcc {
+
+    private static final Logger LOGGER = LoggerFactory.getLogger(GppToolChain.class);
+
+    public static final String DEFAULT_NAME = "gcc";
+
+    private final Factory<ExecAction> execActionFactory;
+    private final Transformer<String, File> versionDeterminer;
+
+    private String version;
+
+    public GppToolChain(String name, OperatingSystem operatingSystem, FileResolver fileResolver, Factory<ExecAction> execActionFactory) {
+        super(name, operatingSystem, new GccToolRegistry(operatingSystem), fileResolver);
+        this.execActionFactory = execActionFactory;
+        this.versionDeterminer = new GppVersionDeterminer();
+
+        tools.setExeName(ToolType.CPP_COMPILER, "g++");
+        tools.setExeName(ToolType.C_COMPILER, "gcc");
+        tools.setExeName(ToolType.ASSEMBLER, "as");
+        tools.setExeName(ToolType.LINKER, "g++");
+        tools.setExeName(ToolType.STATIC_LIB_ARCHIVER, "ar");
+    }
+
+    @Override
+    protected String getTypeName() {
+        return "GNU G++";
+    }
+
+    @Override
+    protected void checkAvailable(ToolChainAvailability availability) {
+        for (ToolType key : ToolType.values()) {
+            availability.mustExist(key.getToolName(), tools.locate(key));
+        }
+        determineVersion();
+        if (version == null) {
+            availability.unavailable("Could not determine G++ version");
+        }
+    }
+
+    public <T extends BinaryToolSpec> Compiler<T> createCppCompiler() {
+        checkAvailable();
+        CommandLineTool<CppCompileSpec> commandLineTool = commandLineTool(ToolType.CPP_COMPILER);
+        return (Compiler<T>) new CppCompiler(commandLineTool, canUseCommandFile());
+    }
+
+    public <T extends BinaryToolSpec> Compiler<T> createCCompiler() {
+        checkAvailable();
+        CommandLineTool<CCompileSpec> commandLineTool = commandLineTool(ToolType.C_COMPILER);
+        return (Compiler<T>) new CCompiler(commandLineTool, canUseCommandFile());
+    }
+
+    public <T extends BinaryToolSpec> Compiler<T> createAssembler() {
+        checkAvailable();
+        CommandLineTool<AssembleSpec> commandLineTool = commandLineTool(ToolType.ASSEMBLER);
+        return (Compiler<T>) new Assembler(commandLineTool);
+    }
+
+    public <T extends LinkerSpec> Compiler<T> createLinker() {
+        checkAvailable();
+        CommandLineTool<LinkerSpec> commandLineTool = commandLineTool(ToolType.LINKER);
+        return (Compiler<T>) new GppLinker(commandLineTool, canUseCommandFile());
+    }
+
+    public <T extends StaticLibraryArchiverSpec> Compiler<T> createStaticLibraryArchiver() {
+        checkAvailable();
+        CommandLineTool<StaticLibraryArchiverSpec> commandLineTool = commandLineTool(ToolType.STATIC_LIB_ARCHIVER);
+        return (Compiler<T>) new ArStaticLibraryArchiver(commandLineTool);
+    }
+
+    private <T extends BinaryToolSpec> CommandLineTool<T> commandLineTool(ToolType key) {
+        CommandLineTool<T> commandLineTool = new CommandLineTool<T>(key.getToolName(), tools.locate(key), execActionFactory);
+        commandLineTool.withPath(getPaths());
+        return commandLineTool;
+    }
+
+    private void determineVersion() {
+        version = determineVersion(tools.locate(ToolType.CPP_COMPILER));
+        if (version == null) {
+            LOGGER.info("Did not find {} on system", ToolType.CPP_COMPILER.getToolName());
+        } else {
+            LOGGER.info("Found {} with version {}", ToolType.CPP_COMPILER.getToolName(), version);
+        }
+    }
+
+    private String determineVersion(File executable) {
+        return executable == null ? null : versionDeterminer.transform(executable);
+    }
+
+    private boolean canUseCommandFile() {
+        String[] components = version.split("\\.");
+        int majorVersion;
+        try {
+            majorVersion = Integer.valueOf(components[0]);
+        } catch (NumberFormatException e) {
+            throw new IllegalStateException(String.format("Unable to determine major g++ version from version number %s.", version), e);
+        }
+        return majorVersion >= 4;
+    }
+
+}
\ No newline at end of file
diff --git a/subprojects/cpp/src/main/groovy/org/gradle/nativecode/toolchain/internal/gpp/version/GppVersionDeterminer.java b/subprojects/cpp/src/main/groovy/org/gradle/nativecode/toolchain/internal/gpp/version/GppVersionDeterminer.java
new file mode 100644
index 0000000..1bacb34
--- /dev/null
+++ b/subprojects/cpp/src/main/groovy/org/gradle/nativecode/toolchain/internal/gpp/version/GppVersionDeterminer.java
@@ -0,0 +1,105 @@
+/*
+ * Copyright 2012 the original author or authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.gradle.nativecode.toolchain.internal.gpp.version;
+
+import org.gradle.api.Transformer;
+import org.gradle.api.internal.file.IdentityFileResolver;
+import org.gradle.internal.Factory;
+import org.gradle.process.ExecResult;
+import org.gradle.process.internal.ExecHandle;
+import org.gradle.process.internal.ExecHandleBuilder;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+import java.io.ByteArrayOutputStream;
+import java.io.File;
+import java.util.regex.Matcher;
+import java.util.regex.Pattern;
+
+/**
+ * Given a File pointing to an (existing) g++ binary, extracts the version number by running with -v and scraping the output.
+ */
+public class GppVersionDeterminer implements Transformer<String, File> {
+
+    private static final Logger LOGGER = LoggerFactory.getLogger(GppVersionDeterminer.class);
+
+    private final Transformer<String, String> outputScraper;
+    private final Transformer<String, File> outputProducer;
+
+    public GppVersionDeterminer() {
+        this(new GppVersionOutputProducer(new Factory<ExecHandleBuilder>() {
+            public ExecHandleBuilder create() {
+                return new ExecHandleBuilder(new IdentityFileResolver());
+            }
+        }), new GppVersionOutputScraper());
+    }
+
+    GppVersionDeterminer(Transformer<String, File> outputProducer, Transformer<String, String> outputScraper) {
+        this.outputProducer = outputProducer;
+        this.outputScraper = outputScraper;
+    }
+
+    static class GppVersionOutputScraper implements Transformer<String, String> {
+        public String transform(String output) {
+            Pattern pattern = Pattern.compile(".*gcc version (\\S+).*", Pattern.DOTALL);
+            Matcher matcher = pattern.matcher(output);
+            if (matcher.matches()) {
+                String scrapedVersion = matcher.group(1);
+                LOGGER.debug("Extracted version {} from g++ -v output", scrapedVersion);
+                return scrapedVersion;
+            } else {
+                LOGGER.warn("Unable to extract g++ version number from \"{}\" with pattern \"{}\"", output, pattern);
+                return null;
+            }
+        }
+    }
+
+    static class GppVersionOutputProducer implements Transformer<String, File> {
+        
+        private final Factory<ExecHandleBuilder> execHandleBuilderFactory;
+
+        GppVersionOutputProducer(Factory<ExecHandleBuilder> execHandleBuilderFactory) {
+            this.execHandleBuilderFactory = execHandleBuilderFactory;
+        }
+
+        public String transform(File gppBinary) {
+            ExecHandleBuilder exec = execHandleBuilderFactory.create();
+            exec.executable(gppBinary.getAbsolutePath());
+            exec.setWorkingDir(gppBinary.getParentFile());
+            exec.args("-v");
+            ByteArrayOutputStream baos = new ByteArrayOutputStream();
+            exec.setErrorOutput(baos);
+            ExecHandle handle = exec.build();
+            ExecResult result = handle.start().waitForFinish();
+
+            int exitValue = result.getExitValue();
+            if (exitValue == 0) {
+                String output = new String(baos.toByteArray());
+                LOGGER.debug("Output from '{} -v {}", gppBinary.getPath(), output);    
+                return output;                
+            } else {
+                LOGGER.warn("Executing '{} -v' return exit code {}, cannot use", gppBinary.getPath(), exitValue);
+                return null;
+            }
+        }
+    }
+
+    public String transform(File gppBinary) {
+        String output = outputProducer.transform(gppBinary);
+        return output == null ? null : outputScraper.transform(output);
+    }
+}
diff --git a/subprojects/cpp/src/main/groovy/org/gradle/nativecode/toolchain/internal/msvcpp/Assembler.java b/subprojects/cpp/src/main/groovy/org/gradle/nativecode/toolchain/internal/msvcpp/Assembler.java
new file mode 100755
index 0000000..14c5d1b
--- /dev/null
+++ b/subprojects/cpp/src/main/groovy/org/gradle/nativecode/toolchain/internal/msvcpp/Assembler.java
@@ -0,0 +1,64 @@
+/*
+ * Copyright 2013 the original author or authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.gradle.nativecode.toolchain.internal.msvcpp;
+
+import org.gradle.api.internal.tasks.SimpleWorkResult;
+import org.gradle.api.internal.tasks.compile.ArgCollector;
+import org.gradle.api.internal.tasks.compile.CompileSpecToArguments;
+import org.gradle.api.internal.tasks.compile.Compiler;
+import org.gradle.api.tasks.WorkResult;
+import org.gradle.nativecode.language.asm.internal.AssembleSpec;
+import org.gradle.nativecode.toolchain.internal.CommandLineTool;
+
+import java.io.File;
+
+class Assembler implements Compiler<AssembleSpec> {
+
+    private final CommandLineTool<AssembleSpec> commandLineTool;
+
+    public Assembler(CommandLineTool<AssembleSpec> commandLineTool) {
+        this.commandLineTool = commandLineTool;
+    }
+
+    public WorkResult execute(AssembleSpec spec) {
+        boolean didWork = false;
+        CommandLineTool<AssembleSpec> commandLineAssembler = commandLineTool.inWorkDirectory(spec.getObjectFileDir());
+        for (File sourceFile : spec.getSource()) {
+            WorkResult result = commandLineAssembler.withArguments(new AssemblerSpecToArguments(sourceFile)).execute(spec);
+            didWork = didWork || result.getDidWork();
+        }
+        return new SimpleWorkResult(didWork);
+    }
+
+
+    private static class AssemblerSpecToArguments implements CompileSpecToArguments<AssembleSpec> {
+        private final File inputFile;
+
+        public AssemblerSpecToArguments(File inputFile) {
+            this.inputFile = inputFile;
+        }
+
+        public void collectArguments(AssembleSpec spec, ArgCollector collector) {
+            for (String rawArg : spec.getArgs()) {
+                collector.args(rawArg);
+            }
+            collector.args("/nologo");
+            collector.args("/c");
+            collector.args(inputFile.getAbsolutePath());
+        }
+    }
+}
diff --git a/subprojects/cpp/src/main/groovy/org/gradle/nativecode/toolchain/internal/msvcpp/CCompiler.java b/subprojects/cpp/src/main/groovy/org/gradle/nativecode/toolchain/internal/msvcpp/CCompiler.java
new file mode 100755
index 0000000..764f25c
--- /dev/null
+++ b/subprojects/cpp/src/main/groovy/org/gradle/nativecode/toolchain/internal/msvcpp/CCompiler.java
@@ -0,0 +1,50 @@
+/*
+ * Copyright 2013 the original author or authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.gradle.nativecode.toolchain.internal.msvcpp;
+
+import org.gradle.api.internal.tasks.compile.ArgCollector;
+import org.gradle.api.internal.tasks.compile.ArgWriter;
+import org.gradle.api.internal.tasks.compile.Compiler;
+import org.gradle.api.tasks.WorkResult;
+import org.gradle.nativecode.language.c.internal.CCompileSpec;
+import org.gradle.nativecode.toolchain.internal.CommandLineCompilerArgumentsToOptionFile;
+import org.gradle.nativecode.toolchain.internal.CommandLineTool;
+
+class CCompiler implements Compiler<CCompileSpec> {
+
+    private final CommandLineTool<CCompileSpec> commandLineTool;
+
+    CCompiler(CommandLineTool<CCompileSpec> commandLineTool) {
+        this.commandLineTool = commandLineTool
+                .withArguments(new CommandLineCompilerArgumentsToOptionFile<CCompileSpec>(
+                ArgWriter.windowsStyleFactory(), new CCompileSpecToArguments()
+        ));
+    }
+
+    public WorkResult execute(CCompileSpec spec) {
+        return commandLineTool.inWorkDirectory(spec.getObjectFileDir()).execute(spec);
+    }
+
+    private static class CCompileSpecToArguments extends GeneralVisualCppCompileSpecToArguments<CCompileSpec> {
+        public void collectArguments(CCompileSpec spec, ArgCollector collector) {
+            // C-compiling options
+            collector.args("/TC");
+
+            super.collectArguments(spec, collector);
+        }
+    }
+}
diff --git a/subprojects/cpp/src/main/groovy/org/gradle/nativecode/toolchain/internal/msvcpp/CppCompiler.java b/subprojects/cpp/src/main/groovy/org/gradle/nativecode/toolchain/internal/msvcpp/CppCompiler.java
new file mode 100755
index 0000000..a06eaba
--- /dev/null
+++ b/subprojects/cpp/src/main/groovy/org/gradle/nativecode/toolchain/internal/msvcpp/CppCompiler.java
@@ -0,0 +1,49 @@
+/*
+ * Copyright 2012 the original author or authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.gradle.nativecode.toolchain.internal.msvcpp;
+
+import org.gradle.api.internal.tasks.compile.ArgCollector;
+import org.gradle.api.internal.tasks.compile.ArgWriter;
+import org.gradle.api.internal.tasks.compile.Compiler;
+import org.gradle.api.tasks.WorkResult;
+import org.gradle.nativecode.language.cpp.internal.CppCompileSpec;
+import org.gradle.nativecode.toolchain.internal.CommandLineCompilerArgumentsToOptionFile;
+import org.gradle.nativecode.toolchain.internal.CommandLineTool;
+
+class CppCompiler implements Compiler<CppCompileSpec> {
+
+    private final CommandLineTool<CppCompileSpec> commandLineTool;
+
+    CppCompiler(CommandLineTool<CppCompileSpec> commandLineTool) {
+        this.commandLineTool = commandLineTool.withArguments(
+                new CommandLineCompilerArgumentsToOptionFile<CppCompileSpec>(ArgWriter.windowsStyleFactory(), new CppCompileSpecToArguments())
+        );
+    }
+
+    public WorkResult execute(CppCompileSpec spec) {
+        return commandLineTool.inWorkDirectory(spec.getObjectFileDir()).execute(spec);
+    }
+
+    private static class CppCompileSpecToArguments extends GeneralVisualCppCompileSpecToArguments<CppCompileSpec> {
+        public void collectArguments(CppCompileSpec spec, ArgCollector collector) {
+            // C++-compiling options
+            collector.args("/TP");
+
+            super.collectArguments(spec, collector);
+        }
+    }
+    }
diff --git a/subprojects/cpp/src/main/groovy/org/gradle/nativecode/toolchain/internal/msvcpp/GeneralVisualCppCompileSpecToArguments.java b/subprojects/cpp/src/main/groovy/org/gradle/nativecode/toolchain/internal/msvcpp/GeneralVisualCppCompileSpecToArguments.java
new file mode 100644
index 0000000..9ba900b
--- /dev/null
+++ b/subprojects/cpp/src/main/groovy/org/gradle/nativecode/toolchain/internal/msvcpp/GeneralVisualCppCompileSpecToArguments.java
@@ -0,0 +1,44 @@
+/*
+ * Copyright 2013 the original author or authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.gradle.nativecode.toolchain.internal.msvcpp;
+
+import org.gradle.api.internal.tasks.compile.ArgCollector;
+import org.gradle.api.internal.tasks.compile.CompileSpecToArguments;
+import org.gradle.nativecode.language.base.internal.NativeCompileSpec;
+
+import java.io.File;
+
+class GeneralVisualCppCompileSpecToArguments<T extends NativeCompileSpec> implements CompileSpecToArguments<T>  {
+    public void collectArguments(T spec, ArgCollector collector) {
+        collector.args("/nologo");
+        for (String macro : spec.getMacros()) {
+            collector.args("/D" + macro);
+        }
+        collector.args(spec.getArgs());
+        collector.args("/c");
+        if (spec.isPositionIndependentCode()) {
+            collector.args("/LD"); // TODO:DAZ Not sure if this has any effect at compile time
+        }
+        for (File file : spec.getIncludeRoots()) {
+            collector.args("/I", file.getAbsolutePath());
+        }
+        for (File file : spec.getSource()) {
+            collector.args(file);
+        }
+    }
+
+}
diff --git a/subprojects/cpp/src/main/groovy/org/gradle/nativecode/toolchain/internal/msvcpp/LibExeStaticLibraryArchiver.java b/subprojects/cpp/src/main/groovy/org/gradle/nativecode/toolchain/internal/msvcpp/LibExeStaticLibraryArchiver.java
new file mode 100755
index 0000000..8e5da69
--- /dev/null
+++ b/subprojects/cpp/src/main/groovy/org/gradle/nativecode/toolchain/internal/msvcpp/LibExeStaticLibraryArchiver.java
@@ -0,0 +1,53 @@
+/*
+ * Copyright 2013 the original author or authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.gradle.nativecode.toolchain.internal.msvcpp;
+
+import org.gradle.api.internal.tasks.compile.ArgCollector;
+import org.gradle.api.internal.tasks.compile.ArgWriter;
+import org.gradle.api.internal.tasks.compile.CompileSpecToArguments;
+import org.gradle.api.internal.tasks.compile.Compiler;
+import org.gradle.api.tasks.WorkResult;
+import org.gradle.nativecode.base.internal.StaticLibraryArchiverSpec;
+import org.gradle.nativecode.toolchain.internal.CommandLineCompilerArgumentsToOptionFile;
+import org.gradle.nativecode.toolchain.internal.CommandLineTool;
+
+import java.io.File;
+
+class LibExeStaticLibraryArchiver implements Compiler<StaticLibraryArchiverSpec> {
+    private final CommandLineTool<StaticLibraryArchiverSpec> commandLineTool;
+
+    public LibExeStaticLibraryArchiver(CommandLineTool<StaticLibraryArchiverSpec> commandLineTool) {
+        this.commandLineTool = commandLineTool
+                .withArguments(new CommandLineCompilerArgumentsToOptionFile<StaticLibraryArchiverSpec>(ArgWriter.windowsStyleFactory(), new LibExeSpecToArguments()
+        ));
+    }
+
+    public WorkResult execute(StaticLibraryArchiverSpec spec) {
+        return commandLineTool.execute(spec);
+    }
+
+    private static class LibExeSpecToArguments implements CompileSpecToArguments<StaticLibraryArchiverSpec> {
+        public void collectArguments(StaticLibraryArchiverSpec spec, ArgCollector collector) {
+            collector.args("/OUT:" + spec.getOutputFile().getAbsolutePath());
+            collector.args("/NOLOGO");
+            collector.args(spec.getArgs());
+            for (File file : spec.getSource()) {
+                collector.args(file.getAbsolutePath());
+            }
+        }
+    }
+}
diff --git a/subprojects/cpp/src/main/groovy/org/gradle/nativecode/toolchain/internal/msvcpp/LinkExeLinker.java b/subprojects/cpp/src/main/groovy/org/gradle/nativecode/toolchain/internal/msvcpp/LinkExeLinker.java
new file mode 100755
index 0000000..417922a
--- /dev/null
+++ b/subprojects/cpp/src/main/groovy/org/gradle/nativecode/toolchain/internal/msvcpp/LinkExeLinker.java
@@ -0,0 +1,63 @@
+/*
+ * Copyright 2013 the original author or authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.gradle.nativecode.toolchain.internal.msvcpp;
+
+import org.gradle.api.internal.tasks.compile.ArgCollector;
+import org.gradle.api.internal.tasks.compile.ArgWriter;
+import org.gradle.api.internal.tasks.compile.CompileSpecToArguments;
+import org.gradle.api.internal.tasks.compile.Compiler;
+import org.gradle.api.tasks.WorkResult;
+import org.gradle.nativecode.base.internal.LinkerSpec;
+import org.gradle.nativecode.base.internal.SharedLibraryLinkerSpec;
+import org.gradle.nativecode.toolchain.internal.CommandLineCompilerArgumentsToOptionFile;
+import org.gradle.nativecode.toolchain.internal.CommandLineTool;
+
+import java.io.File;
+
+class LinkExeLinker implements Compiler<LinkerSpec> {
+
+    private final CommandLineTool<LinkerSpec> commandLineTool;
+
+    public LinkExeLinker(CommandLineTool<LinkerSpec> commandLineTool) {
+        this.commandLineTool = commandLineTool
+                .withArguments(new CommandLineCompilerArgumentsToOptionFile<LinkerSpec>(
+                ArgWriter.windowsStyleFactory(), new VisualCppLinkerSpecArguments()
+        ));
+    }
+
+    public WorkResult execute(LinkerSpec spec) {
+        return commandLineTool.execute(spec);
+    }
+
+    private static class VisualCppLinkerSpecArguments implements CompileSpecToArguments<LinkerSpec> {
+
+        public void collectArguments(LinkerSpec spec, ArgCollector collector) {
+            collector.args(spec.getArgs());
+            collector.args("/OUT:" + spec.getOutputFile().getAbsolutePath());
+            collector.args("/NOLOGO");
+            if (spec instanceof SharedLibraryLinkerSpec) {
+                collector.args("/DLL");
+            }
+            for (File file : spec.getSource()) {
+                collector.args(file.getAbsolutePath());
+            }
+            for (File file : spec.getLibs()) {
+                collector.args(file.getAbsolutePath());
+            }
+        }
+    }
+}
diff --git a/subprojects/cpp/src/main/groovy/org/gradle/nativecode/toolchain/internal/msvcpp/VisualCppToolChain.java b/subprojects/cpp/src/main/groovy/org/gradle/nativecode/toolchain/internal/msvcpp/VisualCppToolChain.java
new file mode 100755
index 0000000..0fefdf9
--- /dev/null
+++ b/subprojects/cpp/src/main/groovy/org/gradle/nativecode/toolchain/internal/msvcpp/VisualCppToolChain.java
@@ -0,0 +1,127 @@
+/*
+ * Copyright 2012 the original author or authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.gradle.nativecode.toolchain.internal.msvcpp;
+
+import org.gradle.api.internal.file.FileResolver;
+import org.gradle.api.internal.tasks.compile.Compiler;
+import org.gradle.internal.Factory;
+import org.gradle.internal.os.OperatingSystem;
+import org.gradle.nativecode.base.internal.*;
+import org.gradle.nativecode.language.asm.internal.AssembleSpec;
+import org.gradle.nativecode.language.c.internal.CCompileSpec;
+import org.gradle.nativecode.language.cpp.internal.CppCompileSpec;
+import org.gradle.nativecode.toolchain.VisualCpp;
+import org.gradle.nativecode.toolchain.internal.CommandLineTool;
+import org.gradle.nativecode.toolchain.internal.ToolRegistry;
+import org.gradle.nativecode.toolchain.internal.ToolType;
+import org.gradle.process.internal.ExecAction;
+
+import java.io.File;
+import java.util.HashMap;
+import java.util.Map;
+
+public class VisualCppToolChain extends AbstractToolChain implements VisualCpp {
+
+    public static final String DEFAULT_NAME = "visualCpp";
+
+    private final Factory<ExecAction> execActionFactory;
+    private final Map<String, String> environment = new HashMap<String, String>();
+
+    private File installDir;
+
+    public VisualCppToolChain(String name, OperatingSystem operatingSystem, FileResolver fileResolver, Factory<ExecAction> execActionFactory) {
+        super(name, operatingSystem, new ToolRegistry(operatingSystem), fileResolver);
+        this.execActionFactory = execActionFactory;
+
+        tools.setExeName(ToolType.CPP_COMPILER, "cl.exe");
+        tools.setExeName(ToolType.C_COMPILER, "cl.exe");
+        tools.setExeName(ToolType.ASSEMBLER, "ml.exe");
+        tools.setExeName(ToolType.LINKER, "link.exe");
+        tools.setExeName(ToolType.STATIC_LIB_ARCHIVER, "lib.exe");
+    }
+
+    @Override
+    protected String getTypeName() {
+        return "Visual C++";
+    }
+
+    @Override
+    protected void checkAvailable(ToolChainAvailability availability) {
+        if (!operatingSystem.isWindows()) {
+            availability.unavailable("Not available on this operating system.");
+            return;
+        }
+        for (ToolType key : ToolType.values()) {
+            availability.mustExist(key.getToolName(), tools.locate(key));
+        }
+    }
+
+    @Override
+    public String getSharedLibraryLinkFileName(String libraryName) {
+        return getSharedLibraryName(libraryName).replaceFirst("\\.dll$", ".lib");
+    }
+
+    public <T extends BinaryToolSpec> Compiler<T> createCppCompiler() {
+        checkAvailable();
+        CommandLineTool<CppCompileSpec> commandLineTool = commandLineTool(ToolType.CPP_COMPILER);
+        return (Compiler<T>) new CppCompiler(commandLineTool);
+    }
+
+    public <T extends BinaryToolSpec> Compiler<T> createCCompiler() {
+        checkAvailable();
+        CommandLineTool<CCompileSpec> commandLineTool = commandLineTool(ToolType.C_COMPILER);
+        return (Compiler<T>) new CCompiler(commandLineTool);
+    }
+
+    public <T extends BinaryToolSpec> Compiler<T> createAssembler() {
+        checkAvailable();
+        CommandLineTool<AssembleSpec> commandLineTool = commandLineTool(ToolType.ASSEMBLER);
+        return (Compiler<T>) new Assembler(commandLineTool);
+    }
+
+    public <T extends LinkerSpec> Compiler<T> createLinker() {
+        checkAvailable();
+        CommandLineTool<LinkerSpec> commandLineTool = commandLineTool(ToolType.LINKER);
+        return (Compiler<T>) new LinkExeLinker(commandLineTool);
+    }
+
+    public <T extends StaticLibraryArchiverSpec> Compiler<T> createStaticLibraryArchiver() {
+        checkAvailable();
+        CommandLineTool<StaticLibraryArchiverSpec> commandLineTool = commandLineTool(ToolType.STATIC_LIB_ARCHIVER);
+        return (Compiler<T>) new LibExeStaticLibraryArchiver(commandLineTool);
+    }
+
+    private <T extends BinaryToolSpec> CommandLineTool<T> commandLineTool(ToolType key) {
+        CommandLineTool<T> commandLineTool = new CommandLineTool<T>(key.getToolName(), tools.locate(key), execActionFactory);
+        commandLineTool.withPath(tools.getPath());
+        commandLineTool.withEnvironment(environment);
+        return commandLineTool;
+    }
+
+    public File getInstallDir() {
+        return installDir;
+    }
+
+    public void setInstallDir(Object installDirPath) {
+        this.installDir = resolve(installDirPath);
+
+        VisualStudioInstall install = new VisualStudioInstall(this.installDir);
+        tools.setPath(install.getPathEntries());
+        environment.clear();
+        environment.putAll(install.getEnvironment());
+    }
+}
diff --git a/subprojects/cpp/src/main/groovy/org/gradle/nativecode/toolchain/internal/msvcpp/VisualStudioInstall.java b/subprojects/cpp/src/main/groovy/org/gradle/nativecode/toolchain/internal/msvcpp/VisualStudioInstall.java
new file mode 100644
index 0000000..03f8c8d
--- /dev/null
+++ b/subprojects/cpp/src/main/groovy/org/gradle/nativecode/toolchain/internal/msvcpp/VisualStudioInstall.java
@@ -0,0 +1,76 @@
+/*
+ * Copyright 2013 the original author or authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.gradle.nativecode.toolchain.internal.msvcpp;
+
+import java.io.File;
+import java.util.*;
+
+public class VisualStudioInstall {
+    private final File installDir;
+    private final File baseDir;
+    private final List<File> pathEntries = new ArrayList<File>();
+    private final Map<String, String> environment = new HashMap<String, String>();
+
+    public VisualStudioInstall(File installDir) {
+        this.installDir = installDir;
+        this.baseDir = locateBaseDir(installDir);
+
+        File vcDir = new File(baseDir, "VC");
+        File sdkDir = new File(baseDir.getParentFile(), "Microsoft SDKs/Windows/v7.0A");
+
+        addPathEntries(
+                new File(baseDir, "Common7/IDE"),
+                new File(vcDir, "bin"),
+                new File(baseDir, "Common7/Tools"),
+                new File(vcDir, "VCPackages"),
+                new File(sdkDir, "Bin")
+        );
+
+        environment.put("INCLUDE", new File(vcDir, "include").getAbsolutePath());
+        environment.put("LIB", new File(vcDir, "lib").getAbsolutePath() + File.pathSeparator + new File(sdkDir, "lib").getAbsolutePath());
+    }
+
+    private File locateBaseDir(File installDir) {
+        // Handle the visual studio install, VC, or VC/bin directories.
+        if (new File(installDir, "cl.exe").isFile()) {
+            return installDir.getParentFile().getParentFile();
+        } else if (new File(installDir, "bin/cl.exe").isFile()) {
+            return installDir.getParentFile();
+        }
+        return installDir;
+    }
+
+    private void addPathEntries(File... entry) {
+        pathEntries.addAll(Arrays.asList(entry));
+    }
+
+    public File getInstallDir() {
+        return installDir;
+    }
+
+    public List<File> getPathEntries() {
+        return pathEntries;
+    }
+
+    public Map<String, String> getEnvironment() {
+        return environment;
+    }
+
+    public boolean isInstalled() {
+        return new File(baseDir, "VC/bin/cl.exe").isFile();
+    }
+}
diff --git a/subprojects/cpp/src/main/groovy/org/gradle/nativecode/toolchain/package-info.java b/subprojects/cpp/src/main/groovy/org/gradle/nativecode/toolchain/package-info.java
new file mode 100644
index 0000000..aa4cf13
--- /dev/null
+++ b/subprojects/cpp/src/main/groovy/org/gradle/nativecode/toolchain/package-info.java
@@ -0,0 +1,20 @@
+/*
+ * Copyright 2013 the original author or authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT 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 that allow C++ tool chains to be configured.
+ */
+package org.gradle.nativecode.toolchain;
diff --git a/subprojects/cpp/src/main/groovy/org/gradle/nativecode/toolchain/plugins/GppCompilerPlugin.groovy b/subprojects/cpp/src/main/groovy/org/gradle/nativecode/toolchain/plugins/GppCompilerPlugin.groovy
new file mode 100644
index 0000000..5514929
--- /dev/null
+++ b/subprojects/cpp/src/main/groovy/org/gradle/nativecode/toolchain/plugins/GppCompilerPlugin.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.nativecode.toolchain.plugins
+import org.gradle.api.Incubating
+import org.gradle.api.Plugin
+import org.gradle.api.Project
+import org.gradle.api.internal.file.FileResolver
+import org.gradle.internal.Factory
+import org.gradle.internal.os.OperatingSystem
+import org.gradle.nativecode.base.ToolChainRegistry
+import org.gradle.nativecode.base.plugins.NativeBinariesPlugin
+import org.gradle.nativecode.toolchain.Gcc
+import org.gradle.nativecode.toolchain.internal.gpp.GppToolChain
+import org.gradle.process.internal.DefaultExecAction
+import org.gradle.process.internal.ExecAction
+
+import javax.inject.Inject
+/**
+ * A {@link Plugin} which makes the <a href="http://gcc.gnu.org/">GNU G++ compiler</a> available for compiling C/C++ code.
+ */
+ at Incubating
+class GppCompilerPlugin implements Plugin<Project> {
+    private final FileResolver fileResolver
+
+    @Inject
+    GppCompilerPlugin(FileResolver fileResolver) {
+        this.fileResolver = fileResolver
+    }
+
+    void apply(Project project) {
+        project.plugins.apply(NativeBinariesPlugin)
+
+        final toolChainRegistry = project.extensions.getByType(ToolChainRegistry)
+        toolChainRegistry.registerFactory(Gcc, { String name ->
+            return new GppToolChain(name, OperatingSystem.current(), fileResolver, new Factory<ExecAction>() {
+                ExecAction create() {
+                    return new DefaultExecAction(fileResolver);
+                }
+            })
+        })
+        toolChainRegistry.registerDefaultToolChain(GppToolChain.DEFAULT_NAME, Gcc)
+    }
+
+}
diff --git a/subprojects/cpp/src/main/groovy/org/gradle/nativecode/toolchain/plugins/MicrosoftVisualCppPlugin.groovy b/subprojects/cpp/src/main/groovy/org/gradle/nativecode/toolchain/plugins/MicrosoftVisualCppPlugin.groovy
new file mode 100755
index 0000000..e4a1a2c
--- /dev/null
+++ b/subprojects/cpp/src/main/groovy/org/gradle/nativecode/toolchain/plugins/MicrosoftVisualCppPlugin.groovy
@@ -0,0 +1,60 @@
+/*
+ * Copyright 2012 the original author or authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+
+
+package org.gradle.nativecode.toolchain.plugins
+import org.gradle.api.Incubating
+import org.gradle.api.Plugin
+import org.gradle.api.Project
+import org.gradle.api.internal.file.FileResolver
+import org.gradle.internal.Factory
+import org.gradle.internal.os.OperatingSystem
+import org.gradle.nativecode.base.ToolChainRegistry
+import org.gradle.nativecode.base.plugins.NativeBinariesPlugin
+import org.gradle.nativecode.toolchain.VisualCpp
+import org.gradle.nativecode.toolchain.internal.msvcpp.VisualCppToolChain
+import org.gradle.process.internal.DefaultExecAction
+import org.gradle.process.internal.ExecAction
+
+import javax.inject.Inject
+/**
+ * A {@link Plugin} which makes the Microsoft Visual C++ compiler available to compile C/C++ code.
+ */
+ at Incubating
+class MicrosoftVisualCppPlugin implements Plugin<Project> {
+    private final FileResolver fileResolver;
+
+    @Inject
+    MicrosoftVisualCppPlugin(FileResolver fileResolver) {
+        this.fileResolver = fileResolver
+    }
+
+    void apply(Project project) {
+        project.plugins.apply(NativeBinariesPlugin)
+
+        def toolChainRegistry = project.extensions.getByType(ToolChainRegistry)
+
+        toolChainRegistry.registerFactory(VisualCpp, { String name ->
+            return new VisualCppToolChain(name, OperatingSystem.current(), fileResolver, new Factory<ExecAction>() {
+                ExecAction create() {
+                    return new DefaultExecAction(fileResolver)
+                }
+            })
+        })
+        toolChainRegistry.registerDefaultToolChain(VisualCppToolChain.DEFAULT_NAME, VisualCpp)
+    }
+}
diff --git a/subprojects/cpp/src/main/groovy/org/gradle/nativecode/toolchain/plugins/package-info.java b/subprojects/cpp/src/main/groovy/org/gradle/nativecode/toolchain/plugins/package-info.java
new file mode 100644
index 0000000..cd5c53a
--- /dev/null
+++ b/subprojects/cpp/src/main/groovy/org/gradle/nativecode/toolchain/plugins/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.
+ */
+
+/**
+ * Integration with the gpp (gcc frontend) compiler.
+ */
+package org.gradle.nativecode.toolchain.plugins;
diff --git a/subprojects/cpp/src/main/groovy/org/gradle/plugins/binaries/BinariesPlugin.java b/subprojects/cpp/src/main/groovy/org/gradle/plugins/binaries/BinariesPlugin.java
deleted file mode 100644
index 07aac93..0000000
--- a/subprojects/cpp/src/main/groovy/org/gradle/plugins/binaries/BinariesPlugin.java
+++ /dev/null
@@ -1,66 +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.plugins.binaries;
-
-import org.gradle.api.Plugin;
-import org.gradle.api.internal.FactoryNamedDomainObjectContainer;
-import org.gradle.internal.reflect.Instantiator;
-import org.gradle.api.internal.ReflectiveNamedDomainObjectFactory;
-import org.gradle.api.internal.project.ProjectInternal;
-import org.gradle.api.plugins.BasePlugin;
-import org.gradle.plugins.binaries.model.Executable;
-import org.gradle.plugins.binaries.model.Library;
-import org.gradle.plugins.binaries.model.internal.DefaultCompilerRegistry;
-import org.gradle.plugins.binaries.model.internal.DefaultExecutable;
-import org.gradle.plugins.binaries.model.internal.DefaultLibrary;
-
-import javax.inject.Inject;
-
-/**
- * temp plugin, not sure what will provide the binaries container and model elements
- */
-public class BinariesPlugin implements Plugin<ProjectInternal> {
-    private final Instantiator instantiator;
-
-    @Inject
-    public BinariesPlugin(Instantiator instantiator) {
-        this.instantiator = instantiator;
-    }
-
-    public void apply(final ProjectInternal project) {
-        project.getPlugins().apply(BasePlugin.class);
-
-        project.getExtensions().create("compilers",
-                DefaultCompilerRegistry.class,
-                instantiator
-        );
-        DefaultCompilerRegistry registry = project.getExtensions().getByType(DefaultCompilerRegistry.class);
-
-        project.getExtensions().create("executables",
-                FactoryNamedDomainObjectContainer.class,
-                Executable.class,
-                instantiator,
-                new ReflectiveNamedDomainObjectFactory<Executable>(DefaultExecutable.class, project, registry)
-        );
-        project.getExtensions().create("libraries",
-                FactoryNamedDomainObjectContainer.class,
-                Library.class,
-                instantiator,
-                new ReflectiveNamedDomainObjectFactory<Library>(DefaultLibrary.class, project, registry)
-        );
-    }
-
-}
\ No newline at end of file
diff --git a/subprojects/cpp/src/main/groovy/org/gradle/plugins/binaries/model/Binary.java b/subprojects/cpp/src/main/groovy/org/gradle/plugins/binaries/model/Binary.java
deleted file mode 100644
index 1dd4a8e..0000000
--- a/subprojects/cpp/src/main/groovy/org/gradle/plugins/binaries/model/Binary.java
+++ /dev/null
@@ -1,39 +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.plugins.binaries.model;
-
-import org.gradle.api.Buildable;
-import org.gradle.api.Named;
-import org.gradle.api.Project;
-import org.gradle.api.DomainObjectSet;
-
-/**
- * Something to be created.
- */
-public interface Binary extends Named, Buildable {
-
-    CompileSpec getSpec();
-
-    /**
-     * Returns the project that this binary is built by.
-     *
-     * @deprecated No replacement
-     */
-    @Deprecated
-    Project getProject();
-    
-    DomainObjectSet<SourceSet> getSourceSets();
-}
diff --git a/subprojects/cpp/src/main/groovy/org/gradle/plugins/binaries/model/CompileSpec.java b/subprojects/cpp/src/main/groovy/org/gradle/plugins/binaries/model/CompileSpec.java
deleted file mode 100644
index f990edb..0000000
--- a/subprojects/cpp/src/main/groovy/org/gradle/plugins/binaries/model/CompileSpec.java
+++ /dev/null
@@ -1,51 +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.plugins.binaries.model;
-
-import org.gradle.api.Named;
-
-import java.io.File;
-
-/**
- * A high level interface to the compiler, specifying what is to be compiled and how.
- */
-public interface CompileSpec extends Named {
-
-    /**
-     * The ultimate output of the compilation.
-     */
-    File getOutputFile();
-    
-    /**
-     * Do the compile.
-     *
-     * @deprecated No replacement
-     */
-    @Deprecated
-    void compile();
-    
-    /**
-     * Configures the spec to include the source set 
-     */
-    // void from(SourceSet sourceSet);
-    /*
-        notes on from():
-        
-        The CompileSpec interface is likely to just have from(SourceSet) which the default impl of which would be to throw
-        unsupported operation exception, with implementations overriding this method to handle different kinds of source sets
-    */
-    
-}
\ No newline at end of file
diff --git a/subprojects/cpp/src/main/groovy/org/gradle/plugins/binaries/model/Compiler.java b/subprojects/cpp/src/main/groovy/org/gradle/plugins/binaries/model/Compiler.java
deleted file mode 100644
index fefdcfa..0000000
--- a/subprojects/cpp/src/main/groovy/org/gradle/plugins/binaries/model/Compiler.java
+++ /dev/null
@@ -1,24 +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.plugins.binaries.model;
-
-import org.gradle.api.Named;
-
-/**
- * A kind of compiler
- */
-public interface Compiler extends Named {
-}
diff --git a/subprojects/cpp/src/main/groovy/org/gradle/plugins/binaries/model/CompilerRegistry.java b/subprojects/cpp/src/main/groovy/org/gradle/plugins/binaries/model/CompilerRegistry.java
deleted file mode 100644
index 019563c..0000000
--- a/subprojects/cpp/src/main/groovy/org/gradle/plugins/binaries/model/CompilerRegistry.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.plugins.binaries.model;
-
-import org.gradle.api.NamedDomainObjectSet;
-
-/**
- * A container for compiler adapters
- */
-public interface CompilerRegistry extends NamedDomainObjectSet<Compiler> {
-
-    /**
-     * Somehow picks what the default compiler to use is.
-     *
-     * @return null when there is no default compiler available.
-     */
-    Compiler getDefaultCompiler();
-}
\ No newline at end of file
diff --git a/subprojects/cpp/src/main/groovy/org/gradle/plugins/binaries/model/Executable.java b/subprojects/cpp/src/main/groovy/org/gradle/plugins/binaries/model/Executable.java
deleted file mode 100644
index 1f64a13..0000000
--- a/subprojects/cpp/src/main/groovy/org/gradle/plugins/binaries/model/Executable.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.plugins.binaries.model;
-
-/**
- * An executable binary
- */
-public interface Executable extends Binary {
-    
-}
\ No newline at end of file
diff --git a/subprojects/cpp/src/main/groovy/org/gradle/plugins/binaries/model/HeaderExportingSourceSet.java b/subprojects/cpp/src/main/groovy/org/gradle/plugins/binaries/model/HeaderExportingSourceSet.java
deleted file mode 100644
index 0725aa6..0000000
--- a/subprojects/cpp/src/main/groovy/org/gradle/plugins/binaries/model/HeaderExportingSourceSet.java
+++ /dev/null
@@ -1,27 +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.plugins.binaries.model;
-
-import org.gradle.api.file.SourceDirectorySet;
-
-/**
- * A source set that exposes headers
- */
-public interface HeaderExportingSourceSet extends SourceSet {
-
-    SourceDirectorySet getExportedHeaders();
-
-}
\ No newline at end of file
diff --git a/subprojects/cpp/src/main/groovy/org/gradle/plugins/binaries/model/Library.java b/subprojects/cpp/src/main/groovy/org/gradle/plugins/binaries/model/Library.java
deleted file mode 100644
index 73dacda..0000000
--- a/subprojects/cpp/src/main/groovy/org/gradle/plugins/binaries/model/Library.java
+++ /dev/null
@@ -1,27 +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.plugins.binaries.model;
-
-import org.gradle.api.file.SourceDirectorySet;
-
-/**
- * A library
- */
-public interface Library extends Binary {
-    LibraryCompileSpec getSpec();
-
-    SourceDirectorySet getHeaders();
-}
\ No newline at end of file
diff --git a/subprojects/cpp/src/main/groovy/org/gradle/plugins/binaries/model/LibraryCompileSpec.java b/subprojects/cpp/src/main/groovy/org/gradle/plugins/binaries/model/LibraryCompileSpec.java
deleted file mode 100644
index 111e091..0000000
--- a/subprojects/cpp/src/main/groovy/org/gradle/plugins/binaries/model/LibraryCompileSpec.java
+++ /dev/null
@@ -1,32 +0,0 @@
-/*
- * Copyright 2012 the original author or authors.
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- *      http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS 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.binaries.model;
-
-/**
- * A high level interface to the compiler, specifying what is to be compiled and how.
- */
-public interface LibraryCompileSpec extends CompileSpec {
-    /**
-     * <p>Returns the <i>install name</i> for the library. This is the location where this library will be installed on the target
-     * system, and where clients of this library should look for the library.
-     *
-     * <p>On Linux systems, this corresponds to the <i>soname</i> for the library.</p>
-     */
-    String getInstallName();
-
-    void setInstallName(String path);
-}
diff --git a/subprojects/cpp/src/main/groovy/org/gradle/plugins/binaries/model/NativeDependencyCapableSourceSet.java b/subprojects/cpp/src/main/groovy/org/gradle/plugins/binaries/model/NativeDependencyCapableSourceSet.java
deleted file mode 100644
index d834aba..0000000
--- a/subprojects/cpp/src/main/groovy/org/gradle/plugins/binaries/model/NativeDependencyCapableSourceSet.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.plugins.binaries.model;
-
-import org.gradle.api.DomainObjectSet;
-
-/**
- * Source set capability
- */
-public interface NativeDependencyCapableSourceSet extends SourceSet {
-    DomainObjectSet<NativeDependencySet> getNativeDependencySets();
-}
\ No newline at end of file
diff --git a/subprojects/cpp/src/main/groovy/org/gradle/plugins/binaries/model/NativeDependencySet.java b/subprojects/cpp/src/main/groovy/org/gradle/plugins/binaries/model/NativeDependencySet.java
deleted file mode 100644
index 5db2b81..0000000
--- a/subprojects/cpp/src/main/groovy/org/gradle/plugins/binaries/model/NativeDependencySet.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.plugins.binaries.model;
-
-import org.gradle.api.file.FileCollection;
-
-/**
- * Models a collection of native type dependencies.
- */
-public interface NativeDependencySet {
-
-    FileCollection getIncludeRoots();
-    FileCollection getFiles();
-
-}
\ No newline at end of file
diff --git a/subprojects/cpp/src/main/groovy/org/gradle/plugins/binaries/model/SourceSet.java b/subprojects/cpp/src/main/groovy/org/gradle/plugins/binaries/model/SourceSet.java
deleted file mode 100644
index 6bba25a..0000000
--- a/subprojects/cpp/src/main/groovy/org/gradle/plugins/binaries/model/SourceSet.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.plugins.binaries.model;
-
-import org.gradle.api.Named;
-
-/**
- * A generic model of a collection of source
- */
-public interface SourceSet extends Named {
-    
-}
\ No newline at end of file
diff --git a/subprojects/cpp/src/main/groovy/org/gradle/plugins/binaries/model/internal/BinaryCompileSpec.java b/subprojects/cpp/src/main/groovy/org/gradle/plugins/binaries/model/internal/BinaryCompileSpec.java
deleted file mode 100644
index 8d0734e..0000000
--- a/subprojects/cpp/src/main/groovy/org/gradle/plugins/binaries/model/internal/BinaryCompileSpec.java
+++ /dev/null
@@ -1,23 +0,0 @@
-/*
- * Copyright 2012 the original author or authors.
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- *      http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS 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.binaries.model.internal;
-
-import org.gradle.api.Buildable;
-import org.gradle.api.internal.tasks.compile.CompileSpec;
-
-public interface BinaryCompileSpec extends org.gradle.plugins.binaries.model.CompileSpec, CompileSpec, Buildable {
-}
diff --git a/subprojects/cpp/src/main/groovy/org/gradle/plugins/binaries/model/internal/BinaryCompileSpecFactory.java b/subprojects/cpp/src/main/groovy/org/gradle/plugins/binaries/model/internal/BinaryCompileSpecFactory.java
deleted file mode 100644
index de9fe6d..0000000
--- a/subprojects/cpp/src/main/groovy/org/gradle/plugins/binaries/model/internal/BinaryCompileSpecFactory.java
+++ /dev/null
@@ -1,27 +0,0 @@
-/*
- * Copyright 2012 the original author or authors.
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- *      http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS 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.binaries.model.internal;
-
-import org.gradle.api.internal.tasks.compile.Compiler;
-import org.gradle.plugins.binaries.model.Binary;
-
-/**
- * This is ugly. Currently, the CompileSpec impls need access to a Compiler, so we need use this interface to provide one at construction time.
- */
-public interface BinaryCompileSpecFactory {
-    BinaryCompileSpec create(Binary binary, Compiler<?> compiler);
-}
diff --git a/subprojects/cpp/src/main/groovy/org/gradle/plugins/binaries/model/internal/CompileSpecFactory.java b/subprojects/cpp/src/main/groovy/org/gradle/plugins/binaries/model/internal/CompileSpecFactory.java
deleted file mode